<!DOCTYPE html>
<html>
<head>
<title>README.md</title>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">

<style>
/* https://github.com/microsoft/vscode/blob/master/extensions/markdown-language-features/media/markdown.css */
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

body {
	font-family: var(--vscode-markdown-font-family, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif);
	font-size: var(--vscode-markdown-font-size, 14px);
	padding: 0 26px;
	line-height: var(--vscode-markdown-line-height, 22px);
	word-wrap: break-word;
}

#code-csp-warning {
	position: fixed;
	top: 0;
	right: 0;
	color: white;
	margin: 16px;
	text-align: center;
	font-size: 12px;
	font-family: sans-serif;
	background-color:#444444;
	cursor: pointer;
	padding: 6px;
	box-shadow: 1px 1px 1px rgba(0,0,0,.25);
}

#code-csp-warning:hover {
	text-decoration: none;
	background-color:#007acc;
	box-shadow: 2px 2px 2px rgba(0,0,0,.25);
}

body.scrollBeyondLastLine {
	margin-bottom: calc(100vh - 22px);
}

body.showEditorSelection .code-line {
	position: relative;
}

body.showEditorSelection .code-active-line:before,
body.showEditorSelection .code-line:hover:before {
	content: "";
	display: block;
	position: absolute;
	top: 0;
	left: -12px;
	height: 100%;
}

body.showEditorSelection li.code-active-line:before,
body.showEditorSelection li.code-line:hover:before {
	left: -30px;
}

.vscode-light.showEditorSelection .code-active-line:before {
	border-left: 3px solid rgba(0, 0, 0, 0.15);
}

.vscode-light.showEditorSelection .code-line:hover:before {
	border-left: 3px solid rgba(0, 0, 0, 0.40);
}

.vscode-light.showEditorSelection .code-line .code-line:hover:before {
	border-left: none;
}

.vscode-dark.showEditorSelection .code-active-line:before {
	border-left: 3px solid rgba(255, 255, 255, 0.4);
}

.vscode-dark.showEditorSelection .code-line:hover:before {
	border-left: 3px solid rgba(255, 255, 255, 0.60);
}

.vscode-dark.showEditorSelection .code-line .code-line:hover:before {
	border-left: none;
}

.vscode-high-contrast.showEditorSelection .code-active-line:before {
	border-left: 3px solid rgba(255, 160, 0, 0.7);
}

.vscode-high-contrast.showEditorSelection .code-line:hover:before {
	border-left: 3px solid rgba(255, 160, 0, 1);
}

.vscode-high-contrast.showEditorSelection .code-line .code-line:hover:before {
	border-left: none;
}

img {
	max-width: 100%;
	max-height: 100%;
}

a {
	text-decoration: none;
}

a:hover {
	text-decoration: underline;
}

a:focus,
input:focus,
select:focus,
textarea:focus {
	outline: 1px solid -webkit-focus-ring-color;
	outline-offset: -1px;
}

hr {
	border: 0;
	height: 2px;
	border-bottom: 2px solid;
}

h1 {
	padding-bottom: 0.3em;
	line-height: 1.2;
	border-bottom-width: 1px;
	border-bottom-style: solid;
}

h1, h2, h3 {
	font-weight: normal;
}

table {
	border-collapse: collapse;
}

table > thead > tr > th {
	text-align: left;
	border-bottom: 1px solid;
}

table > thead > tr > th,
table > thead > tr > td,
table > tbody > tr > th,
table > tbody > tr > td {
	padding: 5px 10px;
}

table > tbody > tr + tr > td {
	border-top: 1px solid;
}

blockquote {
	margin: 0 7px 0 5px;
	padding: 0 16px 0 10px;
	border-left-width: 5px;
	border-left-style: solid;
}

code {
	font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback";
	font-size: 1em;
	line-height: 1.357em;
}

body.wordWrap pre {
	white-space: pre-wrap;
}

pre:not(.hljs),
pre.hljs code > div {
	padding: 16px;
	border-radius: 3px;
	overflow: auto;
}

pre code {
	color: var(--vscode-editor-foreground);
	tab-size: 4;
}

/** Theming */

.vscode-light pre {
	background-color: rgba(220, 220, 220, 0.4);
}

.vscode-dark pre {
	background-color: rgba(10, 10, 10, 0.4);
}

.vscode-high-contrast pre {
	background-color: rgb(0, 0, 0);
}

.vscode-high-contrast h1 {
	border-color: rgb(0, 0, 0);
}

.vscode-light table > thead > tr > th {
	border-color: rgba(0, 0, 0, 0.69);
}

.vscode-dark table > thead > tr > th {
	border-color: rgba(255, 255, 255, 0.69);
}

.vscode-light h1,
.vscode-light hr,
.vscode-light table > tbody > tr + tr > td {
	border-color: rgba(0, 0, 0, 0.18);
}

.vscode-dark h1,
.vscode-dark hr,
.vscode-dark table > tbody > tr + tr > td {
	border-color: rgba(255, 255, 255, 0.18);
}

</style>

<style>
/* Tomorrow Theme */
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
/* Original theme - https://github.com/chriskempson/tomorrow-theme */

/* Tomorrow Comment */
.hljs-comment,
.hljs-quote {
	color: #8e908c;
}

/* Tomorrow Red */
.hljs-variable,
.hljs-template-variable,
.hljs-tag,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class,
.hljs-regexp,
.hljs-deletion {
	color: #c82829;
}

/* Tomorrow Orange */
.hljs-number,
.hljs-built_in,
.hljs-builtin-name,
.hljs-literal,
.hljs-type,
.hljs-params,
.hljs-meta,
.hljs-link {
	color: #f5871f;
}

/* Tomorrow Yellow */
.hljs-attribute {
	color: #eab700;
}

/* Tomorrow Green */
.hljs-string,
.hljs-symbol,
.hljs-bullet,
.hljs-addition {
	color: #718c00;
}

/* Tomorrow Blue */
.hljs-title,
.hljs-section {
	color: #4271ae;
}

/* Tomorrow Purple */
.hljs-keyword,
.hljs-selector-tag {
	color: #8959a8;
}

.hljs {
	display: block;
	overflow-x: auto;
	color: #4d4d4c;
	padding: 0.5em;
}

.hljs-emphasis {
	font-style: italic;
}

.hljs-strong {
	font-weight: bold;
}
</style>

<style>
/*
 * Markdown PDF CSS
 */

 body {
	font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif, "Meiryo";
	padding: 0 12px;
}

pre {
	background-color: #f8f8f8;
	border: 1px solid #cccccc;
	border-radius: 3px;
	overflow-x: auto;
	white-space: pre-wrap;
	overflow-wrap: break-word;
}

pre:not(.hljs) {
	padding: 23px;
	line-height: 19px;
}

blockquote {
	background: rgba(127, 127, 127, 0.1);
	border-color: rgba(0, 122, 204, 0.5);
}

.emoji {
	height: 1.4em;
}

code {
	font-size: 14px;
	line-height: 19px;
}

/* for inline code */
:not(pre):not(.hljs) > code {
	color: #C9AE75; /* Change the old color so it seems less like an error */
	font-size: inherit;
}

/* Page Break : use <div class="page"/> to insert page break
-------------------------------------------------------- */
.page {
	page-break-after: always;
}

</style>

<script src="https://unpkg.com/mermaid/dist/mermaid.min.js"></script>
</head>
<body>
  <script>
    mermaid.initialize({
      startOnLoad: true,
      theme: document.body.classList.contains('vscode-dark') || document.body.classList.contains('vscode-high-contrast')
          ? 'dark'
          : 'default'
    });
  </script>
<h1 id="vuejs-%E5%9F%BA%E7%A1%80">Vue.js 基础</h1>
<h2 id="vuejs-%E7%AE%80%E4%BB%8B">Vue.js 简介</h2>
<blockquote>
<p>传统开发，主要的工作是请求数据、修改 DOM 展示数据，DOM 操作与逻辑操作混合在一起，让代码变得十分臃肿。</p>
<p>Vue.js 是一个前端框架，提供了一套开发规则，学习 Vue.js 可以让代码变得更加简洁、高效。</p>
</blockquote>
<h3 id="vuejs-%E6%A0%B8%E5%BF%83%E7%89%B9%E6%80%A7">Vue.js 核心特性</h3>
<h4 id="%E6%95%B0%E6%8D%AE%E9%A9%B1%E5%8A%A8%E8%A7%86%E5%9B%BE">数据驱动视图</h4>
<p>MVVM ( Model-View-ViewModel ) 是一种软件架构思想，MVVM 指的是将应用划分为3层，分别为 Model 层、View 层、ViewModel 层。</p>
<ul>
<li>Model 层：数据模型，是代码中主要操作的部分。</li>
<li>View 层：视图模板，对应要显示的标签结构。</li>
<li>ViewModel 层：用于处理业务逻辑，会自动将 Model 与 View 结合，暴露 Model 中被 View 使用的数据，并将 Model 变化自动更新到 View，实现数据绑定；对于可输入元素（输入框、文本域等）还可将 View 变化自动更新到 Model ，实现双向数据绑定。</li>
</ul>
<p>优点：</p>
<ul>
<li>View 与 Model 分离，降低耦合度。</li>
<li>解放 DOM 操作。</li>
</ul>
<p>缺点：</p>
<ul>
<li>Bug 调试难度相对变大。</li>
<li>大型项目的 Model 与 View 过多，维护成本较高。</li>
</ul>
<h4 id="%E7%BB%84%E4%BB%B6%E5%8C%96%E5%BC%80%E5%8F%91">组件化开发</h4>
<p>将需要复用的网页功能扩展为 HTML 元素，大大提高开发效率与可维护性。</p>
<h2 id="%E5%9F%BA%E7%A1%80%E6%93%8D%E4%BD%9C">基础操作</h2>
<h3 id="%E5%AE%89%E8%A3%85">安装</h3>
<p>本地引入：</p>
<ul>
<li>开发版本：https://cn.vuejs.org/js/vue.js</li>
<li>生产版本：https://cn.vuejs.org/js/vue.min.js</li>
</ul>
<p>cdn：</p>
<ul>
<li>最新版：https://cdn.jsdelivr.net/npm/vue/dist/vue.js</li>
<li>生产环境（指定版本）：https://cdn.jsdelivr.net/npm/vue@2.6.12</li>
</ul>
<p>npm：</p>
<ul>
<li>
<p>最新稳定版：</p>
<pre class="hljs"><code><div>npm install vue
</div></code></pre>
</li>
<li>
<p>制定版本：</p>
<pre class="hljs"><code><div>npm install vue@版本号
</div></code></pre>
</li>
</ul>
<h3 id="vue%E5%AE%9E%E4%BE%8B">Vue实例</h3>
<blockquote>
<p>Vue 实例，代表的是MVVM 中的 ViewModel，是 Vue 应用的基础。</p>
</blockquote>
<p>创建 Vue 实例后，通过对选项对象进行配置，来设置不同的 Vue 功能。接下来我们将讲解 Vue 实例选项内容。</p>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
   <span class="hljs-comment">// 选项对象 </span>
});
</div></code></pre>
<h3 id="el">el</h3>
<blockquote>
<p>实例选项 el 代表 MVVM 中 View 的容器，用于选择要被 Vue 操作的元素。</p>
</blockquote>
<p>el 可以为 <code>选择器字符串</code> 或 <code> DOM 对象</code> ，代表一个元素，但不能为 html 或 body 。</p>
<p>可通过 vm.$el 访问。</p>
<p>为设置 el 的 vue 实例，也可以通过 vm.$mount() 进行挂载，参数形式与 el 规则相同</p>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>
});
</div></code></pre>
<h3 id="data">data</h3>
<blockquote>
<p>实例选项 data 代表 MVVM 中的 Model，  用于存储数据。</p>
</blockquote>
<p>数据可通过 vm.$data.数据 或 vm.数据 访问。</p>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">title</span>: <span class="hljs-string">'标题文本'</span>,
        <span class="hljs-attr">content</span>: <span class="hljs-string">'内容文本'</span>
    }
});
<span class="hljs-built_in">console</span>.log(vm.$data.title);
<span class="hljs-built_in">console</span>.log(vm.title);
</div></code></pre>
<p>只有声明在 data 中的数据才能在修改后自动更新到 View 中，称为响应式数据。</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{ content }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">title</span>: <span class="hljs-string">'标题文本'</span>,
        <span class="hljs-attr">content</span>: <span class="hljs-string">'内容文本'</span>
    }
});
<span class="hljs-built_in">console</span>.log(vm.$data.title);
<span class="hljs-built_in">console</span>.log(vm.title);
</div></code></pre>
<p>data 中存在数组时，索引操作与 length 操作无法自动更新视图，这时可以借助 Vue.set() 方法替代操作。</p>
<ul>
<li>Vue.set() 参数1为要设置的数据（例如数组），参数2为键（例如索引），参数3为新值。</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">contentArr</span>: [<span class="hljs-string">'内容1'</span>, <span class="hljs-string">'内容2'</span>, <span class="hljs-string">'内容3'</span>]
  }
});

<span class="hljs-comment">// 在控制台中测试代码：</span>
vm.contentArr.length = <span class="hljs-number">0</span>; <span class="hljs-comment">// 无效</span>
vm.contentArr[<span class="hljs-number">1</span>] = <span class="hljs-string">'新内容'</span>; <span class="hljs-comment">// 无效</span>
Vue.set(vm.contentArr, <span class="hljs-number">1</span>, <span class="hljs-string">'生效的新内容'</span>); <span class="hljs-comment">// 生效</span>
</div></code></pre>
<h3 id="%E6%8F%92%E5%80%BC%E8%A1%A8%E8%BE%BE%E5%BC%8F">插值表达式</h3>
<blockquote>
<p>插值表达式用于在 View 中进行模板语法操作，可在标签内容中插入数据或操作代码。</p>
</blockquote>
<p>书写方式为：{{}}。</p>
<p>内部可以书写表达式，但不能书写语句。</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>计算结果为：{{ 1 + 2 +　3 }}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>比较结果为：{{ 2 &gt; 1 ? 2 : 1 }}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<h3 id="methods">methods</h3>
<blockquote>
<p>methods 用于封装需要在实例中使用的函数。</p>
</blockquote>
<p>methods 中的函数内的 this 代表 vm 。</p>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">title</span>: <span class="hljs-string">'标题文本'</span>,
        <span class="hljs-attr">content</span>: <span class="hljs-string">'内容文本'</span>
    },
    <span class="hljs-attr">methods</span>: {
        output () {
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'标题为'</span> + <span class="hljs-keyword">this</span>.title + <span class="hljs-string">','</span> + <span class="hljs-string">'内容为'</span> + <span class="hljs-keyword">this</span>.content);
        }
    }
});
</div></code></pre>
<h2 id="%E6%8C%87%E4%BB%A4">指令</h2>
<blockquote>
<p>在 Vue.js 中，指令的就是以 v- 开头的 html 自定义属性，不同的指令用于设置不同功能。</p>
</blockquote>
<h3 id="%E5%86%85%E5%AE%B9%E5%A4%84%E7%90%86">内容处理</h3>
<h4 id="v-once-%E6%8C%87%E4%BB%A4">v-once 指令</h4>
<blockquote>
<p>用于让元素内部的插值表达式只生效一次。</p>
</blockquote>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>此内容会随数据变化自动更改：{{content}}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">v-once</span>&gt;</span>此内容不会随数据变化自动更改：{{content}}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<h4 id="v-text-%E6%8C%87%E4%BB%A4">v-text 指令</h4>
<blockquote>
<p>用于给标签设置纯文本内容，设置后原内容被覆盖。</p>
</blockquote>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">v-text</span>=<span class="hljs-string">"content"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<h4 id="v-html-%E6%8C%87%E4%BB%A4">v-html 指令</h4>
<blockquote>
<p>用于给标签设置结构文本，设置后原内容被覆盖。</p>
</blockquote>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">v-html</span>=<span class="hljs-string">"content"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<h3 id="%E5%B1%9E%E6%80%A7%E7%BB%91%E5%AE%9A">属性绑定</h3>
<h4 id="v-bind-%E6%8C%87%E4%BB%A4">v-bind 指令</h4>
<blockquote>
<p>插值表达式只能在内容区域使用，如果要动态设置属性，需要使用 v-bind 指令。</p>
</blockquote>
<p>书写为 v-bind:属性 或简写为 :属性</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">v-bind:title</span>=<span class="hljs-string">"title1"</span>&gt;</span>这是一段文本内容<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">:title</span>=<span class="hljs-string">"title2"</span>&gt;</span>这是一段文本内容<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">title1</span>: <span class="hljs-string">'这是第一个p标签'</span>,
    <span class="hljs-attr">title2</span>: <span class="hljs-string">'这是第二个p标签'</span>
  }
});
</div></code></pre>
<p>除了可以单个绑定，还可以通过对象同时绑定多个属性</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">v-bind</span>=<span class="hljs-string">"attrObj"</span>&gt;</span>这是 p 标签的内容<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">attrObj</span>: {
      <span class="hljs-attr">id</span>: <span class="hljs-string">'box'</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">'示例内容'</span>,
      <span class="hljs-attr">class</span>: <span class="hljs-string">'clearFix'</span>,
      <span class="hljs-string">'data-title'</span>: <span class="hljs-string">'这是 data-title 的内容'</span>
    }
  }
});
</div></code></pre>
<h4 id="class-%E7%BB%91%E5%AE%9A">class 绑定</h4>
<blockquote>
<p>由于 class 可绑定多个值，v-bind 为 class 增加了更多功能。</p>
</blockquote>
<p>class 与 :class 可以共存</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"a"</span> <span class="hljs-attr">:class</span>=<span class="hljs-string">"cls"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">cls</span>: <span class="hljs-string">'b'</span>
  }
});
</div></code></pre>
<p>:class 可以设置对象，对象的键表示类名，值为布尔类型，用于控制类名是否显示。</p>
<ul>
<li>带 - 的类型需要加 ' '</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">:class</span>=<span class="hljs-string">"{ b:isB, c: isC, 'class-d': true }"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">isB</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">isC</span>: <span class="hljs-literal">false</span>
  }
});
</div></code></pre>
<p>:class 设置数组时，可以结合前面两种功能的特点</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">:class</span>=<span class="hljs-string">"['a', {b: isB} ,'c']"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">isB</span>: <span class="hljs-literal">true</span>
  }
});
</div></code></pre>
<h4 id="style-%E7%BB%91%E5%AE%9A">style 绑定</h4>
<blockquote>
<p>style 可以设置多个值，v-bind 为 style 增加了更多功能。</p>
</blockquote>
<p>style 与 :style 可以共存，重复时 :style 会覆盖 style, :style 的值为对象，键为属性名，值为属性值。</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"width:100px;"</span> <span class="hljs-attr">:style</span>=<span class="hljs-string">"styleObj"</span>&gt;</span>标签内容<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">styleObj</span>: {
      <span class="hljs-attr">width</span>: <span class="hljs-string">'200px'</span>,
      <span class="hljs-attr">height</span>: <span class="hljs-string">'100px'</span>,
      <span class="hljs-attr">border</span>: <span class="hljs-string">'1px solid #ccc'</span>
    }
  }
});
</div></code></pre>
<p>设置数组值时，可以传入多个样式对象</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"width:100px;"</span> <span class="hljs-attr">:style</span>=<span class="hljs-string">"[styleObj1, styleObj2]"</span>&gt;</span>标签内容<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">styleObj1</span>: {
      <span class="hljs-attr">height</span>: <span class="hljs-string">'100px'</span>,
      <span class="hljs-attr">width</span>: <span class="hljs-string">'200px'</span>
    },
    <span class="hljs-attr">styleObj2</span>: {
      <span class="hljs-attr">border</span>: <span class="hljs-string">'1px solid #ccc'</span>,
      <span class="hljs-attr">color</span>: <span class="hljs-string">'blue'</span>
    }
  }
});
</div></code></pre>
<h3 id="%E6%B8%B2%E6%9F%93%E6%8C%87%E4%BB%A4">渲染指令</h3>
<h4 id="v-for-%E6%8C%87%E4%BB%A4">v-for 指令</h4>
<blockquote>
<p>用于遍历数据生成结构，数组与对象均可操作。</p>
</blockquote>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- 数组遍历 --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"item in arr"</span>&gt;</span>{{item}}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- 数组遍历：含索引 --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"(item, index) in arr"</span>&gt;</span>{{item}}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- 对象遍历 --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"value in obj"</span>&gt;</span>{{value}}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- 对象遍历：含键名 --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"(value, key) in obj"</span>&gt;</span>{{value}}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- 对象遍历：含键名、索引值 --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"(value, key, index) in obj"</span>&gt;</span>{{value}}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
    	<span class="hljs-attr">arr</span>: [<span class="hljs-string">'内容1'</span>, <span class="hljs-string">'内容2'</span>, <span class="hljs-string">'内容3'</span>],
        <span class="hljs-attr">obj</span>: {
            <span class="hljs-attr">content1</span>: <span class="hljs-string">'内容1'</span>,
            <span class="hljs-attr">content2</span>: <span class="hljs-string">'内容2'</span>,
            <span class="hljs-attr">content3</span>: <span class="hljs-string">'内容3'</span>
        }
    }
});
</div></code></pre>
<ul>
<li>
<p>使用 v-for 的同时，应始终指定唯一的 key 属性，可以提高渲染性能并避免问题</p>
<ul>
<li>可通过调整数据位置来演示渲染问题。</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"item in items"</span> <span class="hljs-attr">:key</span>=<span class="hljs-string">"item.id"</span>&gt;</span>{{value}}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
    	<span class="hljs-attr">items</span>: [
            {
                <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>,
                <span class="hljs-attr">content</span>: <span class="hljs-string">'内容1'</span>
            },
            {
                <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>,
                <span class="hljs-attr">content</span>: <span class="hljs-string">'内容2'</span>
            },
            {
                <span class="hljs-attr">id</span>: <span class="hljs-number">3</span>,
                <span class="hljs-attr">content</span>: <span class="hljs-string">'内容3'</span>
            }
        ]
    }
});
</div></code></pre>
</li>
<li>
<p>通过 <code>&lt;template&gt;</code> 标签设置模板占位符，可以将部分元素或内容作为整体进行操作。</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>奇数行<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>偶数行<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
</li>
<li>
<p>遍历对象生成元素后，如希望给对象添加新的属性并自动更新，则需要使用 Vue.set() 设置，普通赋值无效。</p>
<ul>
<li>建议：最好将所有数据提前初始化好</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
  		<span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"value in obj"</span>&gt;</span>{{value}}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
	<span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">obj</span>: {
        <span class="hljs-attr">content1</span>: <span class="hljs-string">'内容1'</span>,
        <span class="hljs-attr">content2</span>: <span class="hljs-string">'内容2'</span>,
        <span class="hljs-attr">content3</span>: <span class="hljs-string">'内容3'</span>
    }
  }
});

<span class="hljs-comment">// 在控制台中测试代码：</span>
vm.obj.content4 = <span class="hljs-string">'内容4'</span>; <span class="hljs-comment">// 无效，注：将错误写法注释后再测试正确代码</span>
Vue.set(vm.obj, <span class="hljs-string">'content4'</span>, <span class="hljs-string">'内容4'</span>)； <span class="hljs-comment">// 生效</span>
</div></code></pre>
</li>
<li>
<p>同样，删除对象属性通过 delete 操作无法触发视图更新 ，需要通过 Vue.delete() 操作</p>
<ul>
<li>
<p>Vue.delete() 参数1为要操作的对象，参数2为属性名</p>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">obj</span>: {
        <span class="hljs-attr">content1</span>: <span class="hljs-string">'内容11'</span>,
        <span class="hljs-attr">content2</span>: <span class="hljs-string">'内容22'</span>,
        <span class="hljs-attr">content3</span>: <span class="hljs-string">'内容33'</span>
    }
  }
});

<span class="hljs-comment">// 在控制台中测试代码：</span>
<span class="hljs-keyword">delete</span> vm.obj.content4; <span class="hljs-comment">// 无效，注：将错误写法注释后再测试正确代码</span>
Vue.delete(vm.obj, <span class="hljs-string">'content4'</span>)； <span class="hljs-comment">// 生效</span>
</div></code></pre>
</li>
</ul>
</li>
</ul>
<h4 id="v-show-%E6%8C%87%E4%BB%A4">v-show 指令</h4>
<blockquote>
<p>用于控制元素显示与隐藏，适用于显示隐藏频繁切换时使用。</p>
</blockquote>
<ul>
<li>值为 true 时元素显示，false 时隐藏。</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">v-show</span>=<span class="hljs-string">"true"</span>&gt;</span>这个元素会显示<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">v-show</span>=<span class="hljs-string">"false"</span>&gt;</span>这个元素会隐藏<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<ul>
<li>由于 v-show 本质为 display 控制的功能，所以无法与 <code>&lt;template&gt;</code> 结合使用。</li>
</ul>
<h4 id="v-if-%E6%8C%87%E4%BB%A4">v-if 指令</h4>
<blockquote>
<p>用于控制元素的创建与移除。</p>
</blockquote>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"2&gt;11"</span>&gt;</span>这个元素不会创建<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">v-else-if</span>=<span class="hljs-string">"2&gt;1"</span>&gt;</span>这个元素会创建<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">v-else</span>&gt;</span>这个元素不会创建<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<p>可通过 v-if 结合 <code>&lt;template&gt;</code> 控制多个同级元素。</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"bool"</span>&gt;</span>
    	<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>元素1<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
		<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>元素2<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<p>给使用 v-if 的同类型元素添加不同的 key 属性</p>
<ul>
<li>由于 Vue.js 默认会尽可能高效的更新 DOM，当通过 v-if 切换两个同类型元素时并不会重新创建，而是对以存在的元素进行修补，但这种操作可能会导致问题，我们可以为元素设置不同的 key 属性。</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"type==='username'"</span>&gt;</span>
    请输入用户名: <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-else</span>&gt;</span>
    请输入邮箱: <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">type</span>: <span class="hljs-string">'username'</span>
  }
});
</div></code></pre>
<p>v-if 与 v-for 一起使用时， v-for 优先级更高，为了提高执行效率，建议将 v-if 设置给父元素。</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"bool"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"item in items"</span> <span class="hljs-attr">:key</span>=<span class="hljs-string">"item.id"</span>&gt;</span>{{value}}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<h3 id="%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86">事件处理</h3>
<h4 id="v-on-%E6%8C%87%E4%BB%A4">v-on 指令</h4>
<blockquote>
<p>用于进行事件绑定。</p>
</blockquote>
<p>书写为 <code>v-on:事件类型名</code> 或简写为 <code>@事件类型名</code>，值为事件处理程序，可书写简单代码或函数</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{ content }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">v-on:click</span>=<span class="hljs-string">"content='新内容1'"</span>&gt;</span>点击修改内容<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"content='新内容2'"</span>&gt;</span>点击修改内容<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<p>处理程序代码较多时，可以在 methods 中设置函数，并设置为事件处理程序</p>
<ul>
<li>注意：函数内的 this 为 Vue 实例</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{ content }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"fn"</span>&gt;</span>点击修改内容<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">content</span>: <span class="hljs-string">'默认内容'</span>,
  },
  <span class="hljs-attr">methods</span>: {
    fn () {
      <span class="hljs-keyword">this</span>.content = <span class="hljs-string">'新内容'</span>;
    }
  }
});
</div></code></pre>
<p>事件对象访问：</p>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">content</span>: <span class="hljs-string">'默认内容'</span>,
  },
  <span class="hljs-attr">methods</span>: {
    fn (event) {
      <span class="hljs-built_in">console</span>.log(event);
    }
  }
});
</div></code></pre>
<p>设置事件时传参，并手动设置事件对象</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{ content }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"fn(content, $event)"</span>&gt;</span>按钮<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">content</span>: <span class="hljs-string">'默认内容'</span>,
  },
  <span class="hljs-attr">methods</span>: {
    fn (content, event) {
      <span class="hljs-built_in">console</span>.log(content);
      <span class="hljs-built_in">console</span>.log(event);
    }
  }
});
</div></code></pre>
<h3 id="%E8%A1%A8%E5%8D%95%E8%BE%93%E5%85%A5%E7%BB%91%E5%AE%9A">表单输入绑定</h3>
<blockquote>
<p>v-model 用于给 <code>&lt;input&gt;</code> 、<code>&lt;textarea&gt;</code> 及 <code>&lt;select&gt;</code> 元素设置双向数据绑定。</p>
</blockquote>
<h4 id="%E6%96%87%E6%9C%AC%E8%BE%93%E5%85%A5%E6%A1%86">文本输入框</h4>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>input内容为：{{ value1 }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"value1"</span>&gt;</span>
    
	<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>textarea内容为：{{ value2 }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"value2"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">value1</span>: <span class="hljs-string">''</span>,
    <span class="hljs-attr">value2</span>: <span class="hljs-string">''</span>
  }
});
</div></code></pre>
<h4 id="%E5%8D%95%E9%80%89%E6%8C%89%E9%92%AE">单选按钮</h4>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>radio数据为： {{ value3 }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"radio"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"one"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"1"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"value3"</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"one"</span>&gt;</span>选项一<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"radio"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"two"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"2"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"value3"</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"two"</span>&gt;</span>选项二<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">value3</span>: <span class="hljs-string">''</span>
  }
});
</div></code></pre>
<h4 id="%E5%A4%8D%E9%80%89%E6%A1%86">复选框</h4>
<ul>
<li>单个复选框绑定时，数据为布尔类型</li>
<li>多个复选框绑定时，数组为数组类型，用于接收多个选项数据。</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>单个checkbox选中的数据为： {{ value4 }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"item"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"选项内容"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"value4"</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"item"</span>&gt;</span>选项<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    
	<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>多个checkbox选中的数据为： {{ value5 }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"one"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"选项一内容"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"value5"</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"one"</span>&gt;</span>选项一<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"two"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"选项二内容"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"value5"</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"two"</span>&gt;</span>选项二<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">value4</span>: <span class="hljs-string">''</span>,
    <span class="hljs-attr">value5</span>: []
  }
});
</div></code></pre>
<h4 id="%E9%80%89%E6%8B%A9%E6%A1%86">选择框</h4>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>单选select数据为： {{ value6 }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">select</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"value6"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">""</span>&gt;</span>请选择<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"1"</span>&gt;</span>选项一<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"2"</span>&gt;</span>选项二<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"3"</span>&gt;</span>选项三<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>多选select数据为： {{ value7 }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">select</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"value7"</span> <span class="hljs-attr">multiple</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"1"</span>&gt;</span>选项一<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"2"</span>&gt;</span>选项二<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"3"</span>&gt;</span>选项三<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">value6</span>: <span class="hljs-string">''</span>,
    <span class="hljs-attr">value7</span>: []
  }
});
</div></code></pre>
<h3 id="%E4%BF%AE%E9%A5%B0%E7%AC%A6">修饰符</h3>
<blockquote>
<p>修饰符是以点开头的指令后缀，用于给当前指令设置特殊操作。</p>
</blockquote>
<h4 id="%E4%BA%8B%E4%BB%B6%E4%BF%AE%E9%A5%B0%E7%AC%A6">事件修饰符</h4>
<blockquote>
<p>事件修饰符用于修饰 v-on 指令</p>
</blockquote>
<ul>
<li>.prevent：用于阻止默认事件行为，相当于 event.preventDefault()</li>
<li>.stop：用于阻止事件传播，相当于 event.stopPropagation()</li>
<li>.once：用于设置事件只会触发一次</li>
<li>修饰符可以连续书写：例如 @click.prevent.once</li>
<li>查询文档学习更多</li>
</ul>
<h4 id="%E6%8C%89%E9%94%AE%E4%BF%AE%E9%A5%B0%E7%AC%A6">按键修饰符</h4>
<blockquote>
<p>按键修饰符用于键盘事件（例如 keyup ）可以由哪种案件触发</p>
</blockquote>
<ul>
<li>.enter 点击回车键</li>
<li>.esc 点击 esc 键</li>
<li>查询文档学习更多</li>
</ul>
<h4 id="%E7%B3%BB%E7%BB%9F%E4%BF%AE%E9%A5%B0%E7%AC%A6">系统修饰符</h4>
<blockquote>
<p>用于实现事件在点击系统按钮时触发。</p>
</blockquote>
<ul>
<li>.ctrl</li>
<li>.alt</li>
<li>.shift</li>
<li>查询文档学习更多</li>
</ul>
<h4 id="%E9%BC%A0%E6%A0%87%E4%BF%AE%E9%A5%B0%E7%AC%A6">鼠标修饰符</h4>
<blockquote>
<p>用于实现事件在点击鼠标指定按钮时触发。</p>
</blockquote>
<ul>
<li>.left 点击鼠标左键</li>
<li>.right 点击鼠标右键</li>
<li>.middle 点击鼠标中键</li>
</ul>
<h4 id="v-model-%E4%BF%AE%E9%A5%B0%E7%AC%A6">v-model 修饰符</h4>
<ul>
<li>
<p>.lazy 用于将 v-model 的触发方式由 input 事件触发更改为 change 事件触发。</p>
</li>
<li>
<p>.number 用于自动将用户输入的值转换为数值类型，如无法被 parseFloat() 转换，则返回原始内容。</p>
</li>
<li>
<p>.trim 用于自动过滤用户输入首尾两端空格</p>
</li>
</ul>
<h3 id="%E8%87%AA%E5%AE%9A%E4%B9%89%E6%8C%87%E4%BB%A4">自定义指令</h3>
<h4 id="%E8%87%AA%E5%AE%9A%E4%B9%89%E5%85%A8%E5%B1%80%E6%8C%87%E4%BB%A4">自定义全局指令</h4>
<blockquote>
<p>自定义全局指令可以被任意 Vue 实例或组件使用。</p>
</blockquote>
<p>Vue.directive() 用于设置全局自定义指令</p>
<ul>
<li>参数1： 指令名称，无需书写 v- , 在视图中使用时添加 v- 使用</li>
<li>参数2： 配置对象，用于设置自定义指令的功能</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-focus</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div>Vue.directive(<span class="hljs-string">'focus'</span>, {
  <span class="hljs-comment">// 当被绑定的元素插入到 DOM 中时执行 inserted</span>
  <span class="hljs-attr">inserted</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">el</span>) </span>{
    <span class="hljs-comment">// el 为设置指令的 DOM 元素</span>
    <span class="hljs-comment">// 通过 DOM 语法设置功能即可，例如设置输入框自动获取焦点</span>
    el.focus();
  }
});
</div></code></pre>
<h4 id="%E8%87%AA%E5%AE%9A%E4%B9%89%E5%B1%80%E9%83%A8%E6%8C%87%E4%BB%A4">自定义局部指令</h4>
<blockquote>
<p>自定义局部指令只能在当前 Vue 实例或组件内部使用。</p>
</blockquote>
<pre class="hljs"><code><div><span class="hljs-keyword">new</span> Vue({
    <span class="hljs-comment">// ...省略其他代码</span>
    <span class="hljs-attr">directives</span>: {
        <span class="hljs-attr">focus</span>: {
    		inserted (el) {
				el.focus();
        	}
		}
    }
});
</div></code></pre>
<h2 id="%E8%BF%87%E6%BB%A4%E5%99%A8">过滤器</h2>
<blockquote>
<p>Vue.js 允许自定义过滤器，可被用于对插值表达式与 v-bind 中的文本进行处理。</p>
</blockquote>
<h3 id="%E5%85%A8%E5%B1%80%E8%BF%87%E6%BB%A4%E5%99%A8">全局过滤器</h3>
<blockquote>
<p>全局过滤器可以被任意 Vue 实例或组件使用。</p>
</blockquote>
<p>过滤器本质为函数，数据自动传入到函数中处理，最终展示的数据为返回值。</p>
<pre class="hljs"><code><div>Vue.filter(<span class="hljs-string">'过滤器名称'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">value</span>) </span>{
    <span class="hljs-comment">// value 为传入到过滤器中的数据</span>
	<span class="hljs-keyword">return</span> <span class="hljs-string">'处理结果'</span>;
});
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
	<span class="hljs-comment">&lt;!-- 在 v-bind 中 --&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-bind:id</span>=<span class="hljs-string">"id | filterId"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    
    <span class="hljs-comment">&lt;!-- 在插值表达式中 --&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{{ content | filterContent }}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    
    <span class="hljs-comment">&lt;!-- 同时设置多个过滤器: 数据会从左到右依次进行过滤 --&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{{ content | filterA | filterB }}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    
    <span class="hljs-comment">&lt;!-- 给过滤器传入多个参数 --&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{{ content | filterContent(par1, par2) }}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<h3 id="%E5%B1%80%E9%83%A8%E8%BF%87%E6%BB%A4%E5%99%A8">局部过滤器</h3>
<blockquote>
<p>局部过滤器只能在当前 Vue 实例或组件内部使用。</p>
</blockquote>
<pre class="hljs"><code><div><span class="hljs-keyword">new</span> Vue({
    <span class="hljs-comment">// ...省略其他代码</span>
    <span class="hljs-attr">filters</span>: {
        过滤器名称: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">value</span>) </span>{
        	<span class="hljs-keyword">return</span> <span class="hljs-string">'处理结果'</span>;
    	}
    }
});
</div></code></pre>
<h2 id="%E8%AE%A1%E7%AE%97%E5%B1%9E%E6%80%A7">计算属性</h2>
<blockquote>
<p>Vue.js 的视图不建议书写复杂逻辑，这样不利于维护，所以提供了计算属性来进行处理。</p>
</blockquote>
<p>计算属性本质本函数，在视图模板中访问函数名时函数会自动调用。</p>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">arr</span>: [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>]
  },
  <span class="hljs-attr">computed</span>: {
    result () {
      <span class="hljs-keyword">var</span> arr = <span class="hljs-keyword">this</span>.arr;
      <span class="hljs-keyword">var</span> sum = <span class="hljs-number">0</span>;
      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i &lt; arr.length; i++) {
        sum += arr[i];
      }
      <span class="hljs-keyword">return</span> sum;
    }
  }
});
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{ result }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{ result }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{ result }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<h3 id="computed-%E4%B8%8E-methods-%E7%9A%84%E5%8C%BA%E5%88%AB">computed 与 methods 的区别</h3>
<ul>
<li>computed 用于计算操作，具有缓存性，初次计算后的结果会被缓存，只有当内部依赖的数据改变后，下次获取 computed 时才会重新执行计算；否则，多次获取 computed 值只会读取缓存的结果，而不会重新执行。</li>
<li>methods 无缓存性，每次调用都会执行，常用于处理非计算操作。</li>
</ul>
<h3 id="%E5%B8%B8%E8%A7%81%E6%93%8D%E4%BD%9C">常见操作</h3>
<ul>
<li>
<p>通过计算属性进行处理，并结合 v-for 遍历计算属性结果，用于避免 v-if 与 v-for 结合使用。</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
	    <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"item in result"</span> <span class="hljs-attr">:key</span>=<span class="hljs-string">"item"</span>&gt;</span>
            数值为:{{ item }}
        <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
	<span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">arr</span>: [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>]
  },
  <span class="hljs-attr">computed</span>: {
    result () {
      <span class="hljs-comment">// 筛选出偶数</span>
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.arr.filter(<span class="hljs-function">(<span class="hljs-params">item</span>) =&gt;</span> item % <span class="hljs-number">2</span> === <span class="hljs-number">0</span>);
    }
  }
});
</div></code></pre>
</li>
</ul>
<h3 id="%E8%AE%A1%E7%AE%97%E5%B1%9E%E6%80%A7%E7%9A%84-setter">计算属性的 setter</h3>
<blockquote>
<p>你会发现，计算属性无法像 methods 一样进行传参，那我们要如何针对不同数据进行操作呢？</p>
</blockquote>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
   <span class="hljs-attr">computed</span>: {
     <span class="hljs-attr">getResult</span>: {
       <span class="hljs-comment">// getter</span>
       <span class="hljs-attr">get</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
         <span class="hljs-comment">// 逻辑代码</span>
       },
       <span class="hljs-comment">// setter</span>
       <span class="hljs-attr">set</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">newValue</span>) </span>{
          <span class="hljs-comment">// 逻辑代码</span>
       }
     }
   } 
});
</div></code></pre>
<h2 id="%E4%BE%A6%E5%90%AC%E5%99%A8">侦听器</h2>
<blockquote>
<p>用于监听数据变化并执行指定操作。</p>
</blockquote>
<pre class="hljs"><code><div><span class="hljs-keyword">new</span> Vue({
	<span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">value</span>: <span class="hljs-string">''</span>
    },
    <span class="hljs-attr">watch</span>: {
    	<span class="hljs-comment">// 侦听器名称为要监视的 data 中的属性名</span>
        value (newValue, oldValue) {
    		<span class="hljs-comment">// newValue 为新数据</span>
    		<span class="hljs-comment">// oldValue 为旧数据</span>
			
		}
    }
});
</div></code></pre>
<h2 id="%E7%BB%BC%E5%90%88%E6%A1%88%E4%BE%8Btodomvc">综合案例：TodoMVC</h2>
<h3 id="%E6%A1%88%E4%BE%8B%E8%AF%B4%E6%98%8E">案例说明</h3>
<ul>
<li>官网：http://todomvc.com/</li>
<li>模板：https://github.com/tastejs/todomvc-app-template.git
<ul>
<li>git 克隆到本地</li>
<li>在项目目录下通过 npm install 安装依赖</li>
</ul>
</li>
</ul>
<h3 id="%E9%9C%80%E6%B1%82%E5%88%86%E6%9E%90">需求分析</h3>
<ul>
<li>
<p>事项列表展示</p>
<ul>
<li>有事项的情况</li>
<li>没有事项的情况</li>
</ul>
</li>
<li>
<p>状态栏展示</p>
<ul>
<li>个数展示</li>
<li>单位处理</li>
</ul>
</li>
<li>
<p>事项状态切换</p>
<ul>
<li>单个事项状态切换</li>
<li>所有事项状态切换
<ul>
<li>全部切换按钮操作</li>
<li>单个事项切换按钮操作</li>
</ul>
</li>
</ul>
</li>
<li>
<p>事项新增</p>
<ul>
<li>内容检测</li>
<li>回车新增</li>
</ul>
</li>
<li>
<p>事项删除</p>
<ul>
<li>单个事项删除</li>
<li>已完成事项删除</li>
</ul>
</li>
<li>
<p>事项编辑</p>
<ul>
<li>
<p>触发编辑</p>
</li>
<li>
<p>取消标记</p>
</li>
<li>
<p>保存编辑</p>
</li>
</ul>
</li>
<li>
<p>事项筛选</p>
<ul>
<li>点击切换显示类别</li>
<li>更新渲染所有事项</li>
</ul>
</li>
<li>
<p>事项数据持久化</p>
<ul>
<li>读取本地存储</li>
<li>更新本地存储</li>
</ul>
</li>
</ul>
<h3 id="%E5%8A%9F%E8%83%BD%E8%AF%B4%E6%98%8E">功能说明</h3>
<h4 id="%E4%BA%8B%E9%A1%B9%E5%88%97%E8%A1%A8%E5%B1%95%E7%A4%BA">事项列表展示</h4>
<ul>
<li>
<p>引入 vue.js 文件，创建 Vue 实例设置挂载元素。</p>
</li>
<li>
<p>在 data 中设置 todos 存储初始数据</p>
<pre class="hljs"><code><div><span class="hljs-keyword">new</span> Vue({
	<span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
	<span class="hljs-attr">data</span>: {
		<span class="hljs-attr">todos</span>: [
			{ <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">'示例内容1'</span>, <span class="hljs-attr">completed</span>: <span class="hljs-literal">false</span> },
			{ <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">'示例内容2'</span>, <span class="hljs-attr">completed</span>: <span class="hljs-literal">true</span> },
			{ <span class="hljs-attr">id</span>: <span class="hljs-number">3</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">'示例内容3'</span>, <span class="hljs-attr">completed</span>: <span class="hljs-literal">true</span> }
		]
	}
})
</div></code></pre>
</li>
<li>
<p>设置事项视图</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"todo in todos"</span> <span class="hljs-attr">:key</span>=<span class="hljs-string">"todo.id"</span> <span class="hljs-attr">:class</span>=<span class="hljs-string">"{completed:todo.completed}"</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"view"</span>&gt;</span>
		<span class="hljs-comment">&lt;!-- 选框 --&gt;</span>
		<span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"toggle"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"todo.completed"</span>&gt;</span>
		<span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>{{ todo.title }}<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
		<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"destroy"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
	<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
	<span class="hljs-comment">&lt;!-- 编辑输入框 --&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"edit"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"Rule the web"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
</div></code></pre>
</li>
<li>
<p>设置有无事项时</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"main"</span> <span class="hljs-attr">v-show</span>=<span class="hljs-string">"todos.length"</span>&gt;</span>
	...
<span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">footer</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"footer"</span> <span class="hljs-attr">v-show</span>=<span class="hljs-string">"todos.length"</span>&gt;</span>
	...
<span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
</div></code></pre>
</li>
</ul>
<h3 id="%E7%8A%B6%E6%80%81%E6%A0%8F%E4%BF%A1%E6%81%AF%E5%B1%95%E7%A4%BA">状态栏信息展示</h3>
<ul>
<li>
<p>个数展示</p>
<pre class="hljs"><code><div>computed: {
	remaining () {
		<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.todos.filter(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">todo</span>) </span>{
			<span class="hljs-keyword">return</span> !todo.completed;
		}).length;
	}
}
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">footer</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"footer"</span> <span class="hljs-attr">v-show</span>=<span class="hljs-string">"todos.length"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"todo-count"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>{{ remaining }}<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> items left<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
	...
<span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
</div></code></pre>
</li>
<li>
<p>单位处理</p>
<pre class="hljs"><code><div>methods: {
	<span class="hljs-attr">pluralize</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">word</span>) </span>{
		<span class="hljs-keyword">return</span> word + (<span class="hljs-keyword">this</span>.remaining === <span class="hljs-number">1</span> ? <span class="hljs-string">''</span> : <span class="hljs-string">'s'</span>);
	}
},
</div></code></pre>
<pre class="hljs"><code><div>
<span class="hljs-tag">&lt;<span class="hljs-name">footer</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"footer"</span> <span class="hljs-attr">v-show</span>=<span class="hljs-string">"todos.length"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"todo-count"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>{{ remaining }}<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> {{ pluralize('item') }} left<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
	...
<span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
</div></code></pre>
</li>
</ul>
<h3 id="%E4%BA%8B%E9%A1%B9%E7%8A%B6%E6%80%81%E5%88%87%E6%8D%A2">事项状态切换</h3>
<h4 id="%E5%8D%95%E4%B8%AA%E4%BA%8B%E9%A1%B9%E5%88%87%E6%8D%A2">单个事项切换</h4>
<blockquote>
<p>单个事项切换可能导致全部切换选框状态变化。</p>
</blockquote>
<pre class="hljs"><code><div>computed: {
	...
	allDone: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
		<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.remaining === <span class="hljs-number">0</span>;
	}
},
</div></code></pre>
<p>单个选框操作时，全部选框会访问 allDone 的 getter ，也就是读取 allDone 数据</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"main"</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"toggle-all"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"toggle-all"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"allDone"</span>&gt;</span>
	...
<span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
</div></code></pre>
<h4 id="%E5%A4%9A%E4%B8%AA%E4%BA%8B%E9%A1%B9%E5%88%87%E6%8D%A2">多个事项切换</h4>
<blockquote>
<p>主动操作全部切换选框，需要给 allDone 设置 setter 来实现</p>
</blockquote>
<pre class="hljs"><code><div>computed: {
	...
	allDone: {
		<span class="hljs-attr">get</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
			<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.remaining === <span class="hljs-number">0</span>;
		},
		<span class="hljs-comment">// 用于设置全部切换选框的切换操作</span>
		<span class="hljs-attr">set</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">value</span>) </span>{
			<span class="hljs-keyword">this</span>.todos.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">todo</span>) </span>{
				todo.completed = value;
			})
		}
	}
},
</div></code></pre>
<h3 id="%E4%BA%8B%E9%A1%B9%E6%96%B0%E5%A2%9E">事项新增</h3>
<pre class="hljs"><code><div>methods: {
	...
	addTodo: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
        <span class="hljs-comment">// 思考此处使用 trim() 与 .trim 修饰符的区别</span>
		<span class="hljs-keyword">var</span> value = <span class="hljs-keyword">this</span>.newTodo;
		<span class="hljs-keyword">if</span> (!<span class="hljs-keyword">this</span>.newTodo) {
			<span class="hljs-keyword">return</span>;
		}
		<span class="hljs-keyword">this</span>.todos.push({ <span class="hljs-attr">id</span>: <span class="hljs-keyword">this</span>.todos.length + <span class="hljs-number">1</span>, <span class="hljs-attr">title</span>: value, <span class="hljs-attr">completed</span>: <span class="hljs-literal">false</span> });
		<span class="hljs-keyword">this</span>.newTodo = <span class="hljs-string">''</span>;
	}
}
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"header"</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>todos<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"new-todo"</span> 
           <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"准备做点什么?"</span> 
           <span class="hljs-attr">autofocus</span> 
           <span class="hljs-attr">v-model.trim</span>=<span class="hljs-string">"newTodo"</span> 
           @<span class="hljs-attr">keyup.enter</span>=<span class="hljs-string">"addTodo"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
</div></code></pre>
<h3 id="%E4%BA%8B%E9%A1%B9%E5%88%A0%E9%99%A4">事项删除</h3>
<h4 id="%E5%8D%95%E4%B8%AA%E5%88%A0%E9%99%A4">单个删除</h4>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"todo in todos"</span> <span class="hljs-attr">:key</span>=<span class="hljs-string">"todo.id"</span> <span class="hljs-attr">:class</span>=<span class="hljs-string">"{completed: todo.completed"</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"view"</span>&gt;</span>
		...
		<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"destroy"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"removeTodo(todo)"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
	<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
	...
<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div>methods: {
    ...
    removeTodo: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">todo</span>) </span>{
        <span class="hljs-keyword">var</span> index = <span class="hljs-keyword">this</span>.todos.indexOf(todo);
        <span class="hljs-keyword">this</span>.todos.splice(index, <span class="hljs-number">1</span>);
    },
}
</div></code></pre>
<h4 id="%E5%B7%B2%E5%AE%8C%E6%88%90%E4%BA%8B%E9%A1%B9%E5%88%A0%E9%99%A4">已完成事项删除</h4>
<ul>
<li>只有待办事项少于总数时，说明存在已完成事项，显示删除按钮</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">footer</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"footer"</span> <span class="hljs-attr">v-show</span>=<span class="hljs-string">"todos.length"</span>&gt;</span>
	...
	<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"clear-completed"</span> <span class="hljs-attr">v-show</span>=<span class="hljs-string">"todos.length &gt; remaining"</span>&gt;</span>Clear completed<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div>methods: {
    ...
    removeCompleted: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">this</span>.todos = <span class="hljs-keyword">this</span>.todos.filter(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">todo</span>) </span>{
            <span class="hljs-keyword">return</span> !todo.completed;
        });
    },
}
</div></code></pre>
<h3 id="%E4%BA%8B%E9%A1%B9%E7%BC%96%E8%BE%91">事项编辑</h3>
<h4 id="%E8%A7%A6%E5%8F%91%E7%BC%96%E8%BE%91">触发编辑</h4>
<ul>
<li>
<p>双击事项时记录正在编辑的 todo 与 todo 的原始内容，被编辑的 li 需要设置 editing 类名，</p>
<pre class="hljs"><code><div>data: {
	...
	editingTodo: <span class="hljs-literal">null</span>,
    <span class="hljs-attr">titleBeforeEdit</span>: <span class="hljs-string">''</span>,
},
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"todo in todos"</span> 
    <span class="hljs-attr">:key</span>=<span class="hljs-string">"todo.id"</span> 
    <span class="hljs-attr">:class</span>=<span class="hljs-string">"{completed: todo.completed, editing: todo === editingTodo}"</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"view"</span>&gt;</span>
		<span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"toggle"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"todo.completed"</span>&gt;</span>
		<span class="hljs-tag">&lt;<span class="hljs-name">label</span> @<span class="hljs-attr">dblclick</span>=<span class="hljs-string">"editTodo(todo)"</span>&gt;</span>{{ todo.title }}<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
		<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"destroy"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
	<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"edit"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
</div></code></pre>
</li>
<li>
<p>保存 todo 原内容，用于取消时还原</p>
<pre class="hljs"><code><div>methods: {
    ...
	editTodo: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">todo</span>) </span>{
		<span class="hljs-keyword">this</span>.editingTodo = todo;
		<span class="hljs-keyword">this</span>.titleBeforeEdit = todo.title;
	}
},
</div></code></pre>
</li>
<li>
<p>触发编辑后，输入框无法自动获取焦点，通过自定义指令进行设置</p>
<pre class="hljs"><code><div>directives: {
	<span class="hljs-string">'todo-focus'</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">el, binding</span>) </span>{
		<span class="hljs-keyword">if</span> (binding.value) {
			el.focus();
		}
	}
}
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"todo in todos"</span> 
    <span class="hljs-attr">:key</span>=<span class="hljs-string">"todo.id"</span> 
    <span class="hljs-attr">:class</span>=<span class="hljs-string">"{completed: todo.completed, editing: todo === editingTodo}"</span>&gt;</span>
	...
	<span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"edit"</span> 
           <span class="hljs-attr">v-model</span>=<span class="hljs-string">"todo.title"</span>
           <span class="hljs-attr">v-todo-focus</span>=<span class="hljs-string">"todo === editingTodo"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
</div></code></pre>
</li>
</ul>
<h4 id="%E5%8F%96%E6%B6%88%E7%BC%96%E8%BE%91">取消编辑</h4>
<ul>
<li>
<p>点击 esc 键取消编辑。</p>
<pre class="hljs"><code><div>methods: {
	...
    <span class="hljs-comment">// 取消编辑</span>
    <span class="hljs-attr">cancelEdit</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">todo</span>) </span>{
        <span class="hljs-keyword">this</span>.editingTodo = <span class="hljs-literal">null</span>;
        todo.title = <span class="hljs-keyword">this</span>.titleBeforeEdit;
    }
}
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"todo in todos"</span> 
    <span class="hljs-attr">:key</span>=<span class="hljs-string">"todo.id"</span> 
    <span class="hljs-attr">:class</span>=<span class="hljs-string">"{completed: todo.completed, editing: todo === editingTodo}"</span>&gt;</span>
	...
	<span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"edit"</span> 
           <span class="hljs-attr">v-todo-focus</span>=<span class="hljs-string">"todo === editingTodo"</span> 
           <span class="hljs-attr">v-model</span>=<span class="hljs-string">"todo.title"</span> 
           @<span class="hljs-attr">keyup.esc</span>=<span class="hljs-string">"cancelEdit(todo)"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
</div></code></pre>
</li>
</ul>
<h4 id="%E4%BF%9D%E5%AD%98%E7%BC%96%E8%BE%91">保存编辑</h4>
<ul>
<li>
<p>点击回车键或失去焦点时保存编辑结果</p>
</li>
<li>
<p>如果内容为空，则删除 todo</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"todo in todos"</span> 
    <span class="hljs-attr">:key</span>=<span class="hljs-string">"todo.id"</span> 
    <span class="hljs-attr">:class</span>=<span class="hljs-string">"{completed: todo.completed, editing: todo === editingTodo}"</span>&gt;</span>
  	...
  	<span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"edit"</span> 
           <span class="hljs-attr">v-todo-focus</span>=<span class="hljs-string">"todo === editingTodo"</span> 
           <span class="hljs-attr">v-model</span>=<span class="hljs-string">"todo.title"</span> 
           @<span class="hljs-attr">keyup.esc</span>=<span class="hljs-string">"cancelEdit(todo)"</span>
           @<span class="hljs-attr">keyup.enter</span>=<span class="hljs-string">"editDone(todo)"</span> 
           @<span class="hljs-attr">blur</span>=<span class="hljs-string">"editDone(todo)"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div>methods: {
	...
	<span class="hljs-comment">// 保存编辑</span>
	editDone (todo) {
        <span class="hljs-keyword">if</span> (!<span class="hljs-keyword">this</span>.editingTodo) {
            <span class="hljs-keyword">return</span>;
        }
        <span class="hljs-keyword">this</span>.editingTodo = <span class="hljs-literal">null</span>;
        todo.title = todo.title.trim();
        <span class="hljs-keyword">if</span> (!todo.title) {
            <span class="hljs-comment">// 删除</span>
            <span class="hljs-keyword">this</span>.removeTodo(todo);
        }
    }
},
</div></code></pre>
</li>
</ul>
<h3 id="%E4%BA%8B%E9%A1%B9%E7%AD%9B%E9%80%89">事项筛选</h3>
<ul>
<li>
<p>记录当前渲染条目类别，点击时切换类别并设置类名</p>
<pre class="hljs"><code><div>data: {
	...
	<span class="hljs-comment">// 记录当前显示的 todo 类别，可设置3种值：all active completed</span>
	<span class="hljs-attr">todoType</span>: <span class="hljs-string">'all'</span>
},
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">footer</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"footer"</span> <span class="hljs-attr">v-show</span>=<span class="hljs-string">"todos.length"</span>&gt;</span>
	...
	<span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"filters"</span>&gt;</span>
		<span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
			<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"javascript:;"</span> 
			<span class="hljs-attr">:class</span>=<span class="hljs-string">"{selected: todoType === 'all'}"</span>
			@<span class="hljs-attr">click</span>=<span class="hljs-string">"todoType = 'all'"</span>
			&gt;</span>All<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
		<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
		<span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
			<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"javascript:;"</span> 
			<span class="hljs-attr">:class</span>=<span class="hljs-string">"{selected: todoType === 'active'}"</span>
			@<span class="hljs-attr">click</span>=<span class="hljs-string">"todoType = 'active'"</span>
			&gt;</span>Active<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
		<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
		<span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
			<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"javascript:;"</span> 
			<span class="hljs-attr">:class</span>=<span class="hljs-string">"{selected: todoType === 'completed'}"</span>
			@<span class="hljs-attr">click</span>=<span class="hljs-string">"todoType = 'completed'"</span>
			&gt;</span>Completed<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
		<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
	<span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
</div></code></pre>
</li>
<li>
<p>更新事项渲染方式为计算属性</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"todo in filteredTodo"</span> <span class="hljs-attr">:key</span>=<span class="hljs-string">"todo.id"</span> <span class="hljs-attr">:class</span>=<span class="hljs-string">"{completed:todo.completed, editing: todo === editingTodo}"</span>&gt;</span>
	...
<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div>computed: {
	filteredTodo () {
		<span class="hljs-keyword">return</span> filters[<span class="hljs-keyword">this</span>.todoType](<span class="hljs-keyword">this</span>.todos);
	},
    ...
}
</div></code></pre>
</li>
<li>
<p>将不同类别数据的筛选函数统一存储在对象中，并根据 todoType 进行调用</p>
<pre class="hljs"><code><div><span class="hljs-keyword">let</span> filters = {
	all (todos) {
		<span class="hljs-keyword">return</span> todos;
	},
	active (todos) {
		<span class="hljs-keyword">return</span> todos.filter(<span class="hljs-function"><span class="hljs-params">todo</span> =&gt;</span> !todo.completed);
	},
	completed (todos) {
		<span class="hljs-keyword">return</span> todos.filter(<span class="hljs-function"><span class="hljs-params">todo</span> =&gt;</span> todo.completed);
	}
};
</div></code></pre>
</li>
<li>
<p>（可选）将前面功能中进行的筛选操作替换为 filters 方法的形式</p>
<pre class="hljs"><code><div>computed: {
    ...
	remaining () {
        <span class="hljs-comment">// return this.todos.filter(todo =&gt; !todo.completed).length; // 原始写法</span>
        <span class="hljs-keyword">return</span> filters[<span class="hljs-string">'active'</span>](<span class="hljs-keyword">this</span>.todos).length; <span class="hljs-comment">// 筛选后更新的写法</span>
    }, 
    ...
}
</div></code></pre>
<pre class="hljs"><code><div>methods: {
	...
    removeCompleted () {
        <span class="hljs-comment">// this.todos = this.todos.filter(todo =&gt;!todo.completed); // 原始写法</span>
        <span class="hljs-keyword">this</span>.todos = filters[<span class="hljs-string">'active'</span>](<span class="hljs-keyword">this</span>.todos); <span class="hljs-comment">// 筛选后更新的写法</span>
    },
    ...
},
</div></code></pre>
</li>
</ul>
<h3 id="%E4%BA%8B%E9%A1%B9%E6%95%B0%E6%8D%AE%E6%8C%81%E4%B9%85%E5%8C%96">事项数据持久化</h3>
<h4 id="%E8%8E%B7%E5%8F%96%E6%9C%AC%E5%9C%B0%E5%AD%98%E5%82%A8">获取本地存储</h4>
<ul>
<li>
<p>封装读取本地存储功能</p>
<pre class="hljs"><code><div><span class="hljs-keyword">const</span> TODOS_KEY = <span class="hljs-string">'todos-vue'</span>;
<span class="hljs-keyword">let</span> todoStorage = {
	<span class="hljs-keyword">get</span> () {
		<span class="hljs-keyword">return</span> <span class="hljs-built_in">JSON</span>.parse(localStorage.getItem(TODOS_KEY)) || [];
	}
};
</div></code></pre>
</li>
<li>
<p>将 todos 更改为本地存储获取</p>
<pre class="hljs"><code><div>data: {
    <span class="hljs-comment">/* todos: [ 
        { id: 1, title: '示例内容1', completed: false },
        { id: 2, title: '示例内容2', completed: false },
        { id: 3, title: '示例内容3', completed: true }
    ], */</span>
    <span class="hljs-attr">todos</span>: todoStorage.get(),
    ...
},
</div></code></pre>
</li>
</ul>
<h4 id="%E6%9B%B4%E6%96%B0%E6%9C%AC%E5%9C%B0%E5%AD%98%E5%82%A8">更新本地存储</h4>
<ul>
<li>
<p>封装更新本地存储功能</p>
<pre class="hljs"><code><div><span class="hljs-keyword">let</span> todoStorage = {
	...
	set (todos) {
		localStorage.setItem(TODOS_KEY, <span class="hljs-built_in">JSON</span>.stringify(todos));
	}
};
</div></code></pre>
</li>
<li>
<p>设置侦听器监视 todos 变化，将数据更新到本地存储</p>
<pre class="hljs"><code><div>watch: {
	<span class="hljs-attr">todos</span>: {
		<span class="hljs-attr">deep</span>: <span class="hljs-literal">true</span>,
		<span class="hljs-attr">handler</span>: todoStorage.set
	}
},
</div></code></pre>
</li>
</ul>
<h2 id="vuejs-%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F">Vue.js 生命周期</h2>
<blockquote>
<p>Vue.js 生命周期指的是 Vue 实例从创建到执行，再到销毁的过程。</p>
</blockquote>
<p>Vue.js 提供了用于在生命周期不同阶段执行的<code>生命周期函数</code>用来在特点阶段设置操作。生命周期函数也称为生命周期钩子。</p>
<h3 id="%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E9%98%B6%E6%AE%B5">生命周期阶段</h3>
<p><img src="./media/lifecycle.png" alt=""></p>
<h3 id="%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E5%87%BD%E6%95%B0">生命周期函数</h3>
<blockquote>
<p>生命周期函数均直接设置在实例的选项对象中。</p>
</blockquote>
<ul>
<li><strong>创建阶段</strong>：(每个实例只能执行一次)
<ul>
<li>beforeCreate：实例初始化之前调用，此时 data 和 methods 都功能尚未初始化。</li>
<li><strong>created</strong>：data 和 methods 等功能已经创建好，可以被访问了，数据操作通常在这个阶段。</li>
<li>beforeMount：实例挂载开始之前调用，模板内容还没有被添加到 DOM 中</li>
<li><strong>mounted</strong>：实例挂载完毕，可以通过 vm.$el 访问挂载元素，DOM 操作通常在这个阶段。</li>
</ul>
</li>
<li><strong>运行阶段</strong>：（按需调用，可执行任意次数）
<ul>
<li>beforeUpdate：数据更新时调用，Vue.js 内部还未进行视图更新处理，可以访问更新前的 DOM。</li>
<li>updated：DOM 更新完毕，视图和数据都是最新的。</li>
</ul>
</li>
<li><strong>销毁阶段</strong>：(每个实例只能执行一次)
<ul>
<li>beforeDestroy：实例销毁之前调用。在这一步，实例仍然完全可用。</li>
<li>destroyed：实例销毁后调用，实例的所有功能都已移除销毁。</li>
</ul>
</li>
</ul>
<h1 id="vuejs-%E7%BB%84%E4%BB%B6">Vue.js 组件</h1>
<blockquote>
<p>组件是可复用的 Vue 实例，内部封装组件的结构、样式、逻辑代码，可通过自定义的组件名称进行复用，形式为自定义 HTML 标签。</p>
</blockquote>
<h2 id="%E7%BB%84%E4%BB%B6%E6%B3%A8%E5%86%8C">组件注册</h2>
<h3 id="%E5%85%A8%E5%B1%80%E6%B3%A8%E5%86%8C">全局注册</h3>
<blockquote>
<p>全局注册的组件在注册之后可以用在任意实例或组件中。</p>
</blockquote>
<pre class="hljs"><code><div>Vue.component(<span class="hljs-string">'组件名'</span>, { <span class="hljs-comment">/* 选项对象 */</span> });
</div></code></pre>
<p>注意：全局注册必须设置在根 Vue 实例创建之前。</p>
<h3 id="%E7%BB%84%E4%BB%B6%E5%9F%BA%E7%A1%80">组件基础</h3>
<blockquote>
<p>由于组件是可复用的 Vue 实例，所以它们可与 <code>new Vue</code> 接收相同的选项，例如 <code>data</code>、<code>computed</code>、<code>watch</code>、<code>methods</code> 以及生命周期钩子等。仅有的例外是像 <code>el</code> 这样根实例特有的选项。</p>
</blockquote>
<h4 id="%E7%BB%84%E4%BB%B6%E5%91%BD%E5%90%8D%E8%A7%84%E5%88%99">组件命名规则</h4>
<blockquote>
<p>组件命名有两种方式，但无论采用哪种方式定义，<strong>在 DOM 中都只有 kebab-case 可以使用</strong>。</p>
</blockquote>
<ul>
<li>kebab-case：'my-component-name'</li>
<li>PascalCase：'MyComponentName'</li>
</ul>
<pre class="hljs"><code><div>Vue.component(<span class="hljs-string">'my-component-a'</span>, { <span class="hljs-comment">/* 选项对象 */</span> });
Vue.component(<span class="hljs-string">'my-component-b'</span>, { <span class="hljs-comment">/* 选项对象 */</span> });
Vue.component(<span class="hljs-string">'MyComponentC'</span>, { <span class="hljs-comment">/* 选项对象 */</span> });
Vue.component(<span class="hljs-string">'MyComponentD'</span>, { <span class="hljs-comment">/* 选项对象 */</span> });
</div></code></pre>
<h4 id="template-%E9%80%89%E9%A1%B9">template 选项</h4>
<blockquote>
<p>template 选项用于设置组件结构，最终会被根实例或其他组件引入。</p>
</blockquote>
<p>下面的示例中使用了模板字符串，考虑兼容性时应改为普通字符串。</p>
<pre class="hljs"><code><div><span class="hljs-comment">// 子组件</span>
Vue.component(<span class="hljs-string">'MyComponentA'</span>, {
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div&gt;
      &lt;h3&gt;组件 A 的标题内容&lt;/h3&gt;
    &lt;/div&gt;`</span>,
});
</div></code></pre>
<p><strong>注意：组件必须只有一个根元素</strong>，否则会报错。</p>
<h4 id="data-%E9%80%89%E9%A1%B9">data 选项</h4>
<blockquote>
<p>data 选项必须为函数，而不是像根实例一样设置为对象。</p>
</blockquote>
<p>这种实现方式是为了确保每个组件实例可以维护一份被返回对象的独立的拷贝，不会相互影响。</p>
<pre class="hljs"><code><div>Vue.component(<span class="hljs-string">'MyComA'</span>, {
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
	&lt;h3&gt;{{ title }}&lt;/h3&gt;
  `</span>,
  <span class="hljs-attr">data</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">title</span>: <span class="hljs-string">'示例内容'</span>
    }
  }
})
</div></code></pre>
<h3 id="%E5%B1%80%E9%83%A8%E6%B3%A8%E5%86%8C">局部注册</h3>
<blockquote>
<p>局部注册的组件只能用在当前实例或组件中。</p>
</blockquote>
<pre class="hljs"><code><div><span class="hljs-keyword">new</span> Vue({
    ...
    components: {
        <span class="hljs-string">'my-component-a'</span>: {
            <span class="hljs-attr">template</span>: <span class="hljs-string">'&lt;h3&gt;{{ title }}&lt;/h3&gt;'</span>,
            data () {
                <span class="hljs-keyword">return</span> { <span class="hljs-attr">title</span>: <span class="hljs-string">'a 组件示例内容'</span> }
            }
        },
        <span class="hljs-string">'my-component-b'</span>: {
            <span class="hljs-attr">template</span>: <span class="hljs-string">'&lt;h3&gt;{{ title }}&lt;/h3&gt;'</span>,
            data () {
                <span class="hljs-keyword">return</span> { <span class="hljs-attr">title</span>: <span class="hljs-string">'b 组件示例内容'</span> }
            }
        }
    }
});
</div></code></pre>
<p>你可能注意到，上面的方式将根实例与多个组件的选项对象书写在一起非常不清晰，所以我们通常会将组件的选项对象单独书写，再设置到某个实例的 components 属性中。</p>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> MyComponentA = { <span class="hljs-comment">/* ... */</span> }
<span class="hljs-keyword">var</span> MyComponentB = { <span class="hljs-comment">/* ... */</span> }

<span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">components</span>: {
    <span class="hljs-string">'my-component-a'</span>: MyComponentA,
    <span class="hljs-string">'my-component-b'</span>: MyComponentB
  }
});
</div></code></pre>
<p>或者采用 ES6 的对象属性简写方式</p>
<pre class="hljs"><code><div><span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">components</span>: {
    MyComponentA,
    MyComponentB
  }
});
</div></code></pre>
<h2 id="%E7%BB%84%E4%BB%B6%E9%80%9A%E4%BF%A1">组件通信</h2>
<blockquote>
<p>当我们将页面功能设置为多个子组件后，这些子组件如何使用根实例中的数据呢？子组件如何将操作状态反馈给父实例呢？这时就需要组件间进行传值，这种操作称为组件通信。</p>
</blockquote>
<h3 id="%E7%88%B6%E7%BB%84%E4%BB%B6%E5%90%91%E5%AD%90%E7%BB%84%E4%BB%B6%E4%BC%A0%E5%80%BC">父组件向子组件传值</h3>
<blockquote>
<p>父向子传值需要通过给子组件设置 props 选项实现。</p>
</blockquote>
<h4 id="props">Props</h4>
<p>props 选项为数组，子组件通过 props 定义任意属性后，父组件再给子组件设置对应属性即可。</p>
<blockquote>
<p>注意，props 的属性可以像 data 的属性一样可以在子组件中通过 this 访问或在视图模板中通过名称使用，所以 props <strong>不要</strong>与 data 存在同名属性。</p>
</blockquote>
<pre class="hljs"><code><div><span class="hljs-comment">// 子组件</span>
Vue.component(<span class="hljs-string">'my-component'</span>, {
  <span class="hljs-attr">props</span>: [<span class="hljs-string">'title'</span>],
  <span class="hljs-attr">template</span>: <span class="hljs-string">'&lt;h3&gt;{{ title }}&lt;/h3&gt;'</span>
});
</div></code></pre>
<p>设置方式有2种：<code>静态属性值</code>设置方式与 <code>v-bind 动态绑定</code> 方式，静态属性只能为字符串类型，而动态绑定可以为<strong>任意</strong>类型。</p>
<pre class="hljs"><code><div><span class="hljs-comment">&lt;!-- 父组件 --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">my-component-a</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"示例内容1"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">my-component-a</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">my-component-a</span> <span class="hljs-attr">:title</span>=<span class="hljs-string">"'示例内容2'"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">my-component-a</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">my-component-a</span> <span class="hljs-attr">:title</span>=<span class="hljs-string">"item.title"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">my-component-a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">item</span>: {
      <span class="hljs-attr">title</span>: <span class="hljs-string">'父组件中的数据'</span>
    }
  }
});
</div></code></pre>
<p>注意：prop 命名推荐使用 <strong>camelCase</strong>，但由于 HTML 属性不区分大小写，所以 props 中的 camelCase 属性在被父组件设置时需要使用对应的 <strong>kebab-case</strong>。</p>
<pre class="hljs"><code><div><span class="hljs-comment">// 子组件</span>
Vue.component(<span class="hljs-string">'my-component'</span>, {
  <span class="hljs-attr">props</span>: [<span class="hljs-string">'myTitle'</span>],
  <span class="hljs-attr">template</span>: <span class="hljs-string">'&lt;h3&gt;{{ myTitle }}&lt;/h3&gt;'</span>
});
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-comment">&lt;!-- 父组件 --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">my-component-a</span> <span class="hljs-attr">my-title</span>=<span class="hljs-string">"示例内容1"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">my-component-a</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">my-component-a</span> <span class="hljs-attr">:my-title</span>=<span class="hljs-string">"'示例内容2'"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">my-component-a</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">my-component-a</span> <span class="hljs-attr">:my-title</span>=<span class="hljs-string">"item.title"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">my-component-a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<p>通过 v-for 指令遍历创建组件并传值练习一下吧（注意：key 需要绑定，但无需 props 接收）</p>
<h4 id="%E5%8D%95%E5%90%91%E6%95%B0%E6%8D%AE%E6%B5%81">单向数据流</h4>
<blockquote>
<p>父子组件间的所有 prop 都是 <strong>单向下行绑定</strong> 的。</p>
</blockquote>
<p>每次父级组件发生变更时，子组件所有的 prop 都会刷新为最新值，但反过来则不行。这样可以防止子组件意外变更父级组件的状态，从而导致应用的数据流向难以理解。</p>
<p>如果子组件需要对父组件传递的数据进行处理，应当<strong>定义本地的 data 属性</strong>以保存需要处理的 prop。</p>
<pre class="hljs"><code><div><span class="hljs-comment">// 子组件</span>
Vue.component(<span class="hljs-string">'my-component'</span>, {
  <span class="hljs-attr">props</span>: [<span class="hljs-string">'initialTitle'</span>],
  <span class="hljs-attr">template</span>: <span class="hljs-string">'&lt;h3&gt;{{ myTitle }}&lt;/h3&gt;'</span>,
  data () {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">myTitle</span>: <span class="hljs-keyword">this</span>.initialTitle
    }
  }
});
</div></code></pre>
<p>需要<strong>特别注意</strong>的是，由于对象和数组是通过引用传入的，所以对于一个<strong>数组或对象类型的 prop</strong> 来说，在子组件中变更这个对象或数组内部的数据 <strong>将会</strong> 影响到父组件的状态。</p>
<h4 id="props-%E7%B1%BB%E5%9E%8B">Props 类型</h4>
<blockquote>
<p>Prop 可以设置类型检查，这时需要将 props 更改为一个带有验证需求的对象。</p>
</blockquote>
<p>类型检查的设置方式有两种：</p>
<ul>
<li>第一种：指定单个类型，设置为对应的构造函数即可，无需验证时设置 null 与 undefined 。</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-comment">// 子组件</span>
Vue.component(<span class="hljs-string">'MyComponentA'</span>, {
  <span class="hljs-attr">props</span>: {
    <span class="hljs-attr">parStr</span>: <span class="hljs-built_in">String</span>,
    <span class="hljs-attr">parNum</span>: <span class="hljs-built_in">Number</span>,
    <span class="hljs-attr">parArr</span>: <span class="hljs-built_in">Array</span>,
    <span class="hljs-attr">parObj</span>: <span class="hljs-built_in">Object</span>,
    <span class="hljs-attr">parAny</span>: <span class="hljs-literal">null</span> <span class="hljs-comment">// parAny: undefined</span>
  },
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div&gt;
      {{ parStr }}
      {{ parNum }}
      {{ parArr }}
      {{ parObj }}
      {{ parAny }}
    &lt;/div&gt;
  `</span>
});
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-comment">// 父组件</span>
<span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">str</span>: <span class="hljs-string">'示例内容'</span>,
    <span class="hljs-attr">num</span>: <span class="hljs-number">100</span>,
    <span class="hljs-attr">arr</span>: [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>],
    <span class="hljs-attr">obj</span>: {
      <span class="hljs-attr">content1</span>: <span class="hljs-string">'父组件示例示例内容1'</span>,
      <span class="hljs-attr">content2</span>: <span class="hljs-string">'父组件示例示例内容2'</span>
    },
    <span class="hljs-attr">any</span>: <span class="hljs-string">'任意类型均可'</span>
  }
});
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-comment">&lt;!-- 父组件 --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">my-component-a</span> 
    <span class="hljs-attr">:par-str</span>=<span class="hljs-string">"str"</span>
    <span class="hljs-attr">:par-num</span>=<span class="hljs-string">"num"</span>
    <span class="hljs-attr">:par-arr</span>=<span class="hljs-string">"arr"</span>
    <span class="hljs-attr">:par-obj</span>=<span class="hljs-string">"obj"</span>
    <span class="hljs-attr">:par-any</span>=<span class="hljs-string">"any"</span>
  &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">my-component-a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<p>第二种：指定多个类型，可以将 prop 对应类型设置为数组。</p>
<pre class="hljs"><code><div><span class="hljs-comment">// 子组件</span>
Vue.component(<span class="hljs-string">'MyComponentA'</span>, {
  <span class="hljs-attr">props</span>: {
    <span class="hljs-attr">parData</span>: [<span class="hljs-built_in">String</span>, <span class="hljs-built_in">Number</span>]
  },
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div&gt;
      {{ parData }}
    &lt;/div&gt;
  `</span>
});
</div></code></pre>
<h4 id="props-%E9%AA%8C%E8%AF%81">Props 验证</h4>
<blockquote>
<p>如果需要给一个 Prop 设置多种规则，可以将某个 Prop 的值设置为选项对象</p>
</blockquote>
<p>Prop 值设置为选项对象后，类型检测通过 type 选项进行设置。</p>
<pre class="hljs"><code><div><span class="hljs-comment">// 子组件</span>
Vue.component(<span class="hljs-string">'MyComponentA'</span>, {
  <span class="hljs-attr">props</span>: {
    <span class="hljs-attr">parNum</span>: {
      <span class="hljs-attr">type</span>: <span class="hljs-built_in">Number</span>
    },
    <span class="hljs-attr">parData</span>: {
      <span class="hljs-attr">type</span>: [<span class="hljs-built_in">String</span>, <span class="hljs-built_in">Boolean</span>]
    }
  },
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div&gt;
	  {{ parNum }}
      {{ parData }}
    &lt;/div&gt;
  `</span>
});
</div></code></pre>
<p>required 用于设置数据为必填项。</p>
<pre class="hljs"><code><div><span class="hljs-comment">// 子组件</span>
Vue.component(<span class="hljs-string">'MyComponentA'</span>, {
  <span class="hljs-attr">props</span>: {
    <span class="hljs-attr">parNum</span>: {
      <span class="hljs-attr">type</span>: <span class="hljs-built_in">Number</span>,
      <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>
    }
  },
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div&gt;
      {{ parNum }}
    &lt;/div&gt;
  `</span>
});
</div></code></pre>
<p>default 用于给可选项指定默认值，当父组件未传递数据时生效。</p>
<pre class="hljs"><code><div><span class="hljs-comment">// 子组件</span>
Vue.component(<span class="hljs-string">'MyComponentA'</span>, {
  <span class="hljs-attr">props</span>: {
    <span class="hljs-attr">parNum</span>: {
      <span class="hljs-attr">type</span>: <span class="hljs-built_in">Number</span>,
      <span class="hljs-attr">default</span>: <span class="hljs-number">100</span>
    }
  },
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div&gt;
      {{ parNum }}
    &lt;/div&gt;
  `</span>
});
</div></code></pre>
<p>注意：当默认值为数组或对象时，必须为工厂函数返回的形式（为了确保多个实例数据独立）。</p>
<pre class="hljs"><code><div><span class="hljs-comment">// 子组件</span>
Vue.component(<span class="hljs-string">'MyComponentA'</span>, {
  <span class="hljs-attr">props</span>: {
    <span class="hljs-attr">parArr</span>: {
      <span class="hljs-attr">type</span>: <span class="hljs-built_in">Array</span>,
      <span class="hljs-attr">default</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">return</span> [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
      }
    }
  },
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div&gt;
      {{ parArr }}
    &lt;/div&gt;
  `</span>
});
</div></code></pre>
<p>validator 用于给传入的 prop 设置校验函数，return 值为 false 时 Vue.js 会发出警告。</p>
<pre class="hljs"><code><div><span class="hljs-comment">// 子组件</span>
Vue.component(<span class="hljs-string">'MyComponentA'</span>, {
  <span class="hljs-attr">props</span>: {
    <span class="hljs-attr">parStr</span>: {
      <span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>,
      <span class="hljs-attr">validator</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">value</span>) </span>{
        <span class="hljs-keyword">return</span> value.startsWith(<span class="hljs-string">'lagou'</span>);
      }
    }
  },
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div&gt;
      {{ parStr }}
    &lt;/div&gt;
  `</span>
});
</div></code></pre>
<p>注意：由于 prop 会在实例创建前进行验证，所以 default 与 validator 无法使用例如 data、methods 等功能。</p>
<h4 id="%E9%9D%9E-prop-%E5%B1%9E%E6%80%A7">非 Prop 属性</h4>
<blockquote>
<p>当父组件给子组件设置了属性，但此属性在 props 中不存在，这时会自动绑定到子组件的根元素上。</p>
</blockquote>
<pre class="hljs"><code><div><span class="hljs-comment">&lt;!-- 父组件 --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">my-component-a</span>
    <span class="hljs-attr">demo-attr</span>=<span class="hljs-string">"示例属性"</span>
    <span class="hljs-attr">title</span>=<span class="hljs-string">"示例title"</span>
    <span class="hljs-attr">style</span>=<span class="hljs-string">"height: 200px"</span>
    <span class="hljs-attr">class</span>=<span class="hljs-string">"colorBlue"</span>
  &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">my-component-a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-comment">&lt;!-- 子组件 --&gt;</span>
Vue.component('MyComponentA', {
  template: `<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>子组件内容<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>`
});
</div></code></pre>
<p>如果组件根元素已经存在了对应属性，则父组件的提供的值会<strong>替换</strong>组件内部的值。但 class 与 style 例外，当内外都设置时，属性会自动<strong>合并</strong>。</p>
<pre class="hljs"><code><div>/* 
	修改子组件为以下代码后：
		title 被覆盖，class 与 style 则自动合并。
*/
Vue.component('MyComponentA', {
  template: `
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"原始title"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fl"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"width: 200px;"</span>&gt;</span>子组件内容<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  `
});
</div></code></pre>
<p>如果不希望继承父组件设置的属性，可以设置 inheritAttrs: false，但 class 与 style 不受影响。</p>
<pre class="hljs"><code><div>Vue.component('MyComponentA', {
  inheritAttrs: false,
  template: `
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"原始title"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fl"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"width: 200px;"</span>&gt;</span>子组件内容<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  `
});
</div></code></pre>
<h3 id="%E5%AD%90%E7%BB%84%E4%BB%B6%E5%90%91%E7%88%B6%E7%BB%84%E4%BB%B6%E4%BC%A0%E5%80%BC">子组件向父组件传值</h3>
<h4 id="%E8%87%AA%E5%AE%9A%E4%B9%89%E4%BA%8B%E4%BB%B6">自定义事件</h4>
<blockquote>
<p>子向父传值需要通过给子组件设置自定义事件来实现。</p>
</blockquote>
<p>例如电商网站的购物车功能中，每个商品的个数需要在购物车中进行统计，如果将商品看作子组件，购物车为父组件，那数据的传递过程就是在通过子组件向父组件传值。</p>
<pre class="hljs"><code><div><span class="hljs-comment">&lt;!-- 父组件 --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>购物车<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">product-item</span> 
    <span class="hljs-attr">v-for</span>=<span class="hljs-string">"product in products"</span>
    <span class="hljs-attr">:title</span>=<span class="hljs-string">"product.title"</span>
    <span class="hljs-attr">:key</span>=<span class="hljs-string">"product.id"</span>
  &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">product-item</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>总数为：{{ totalCount }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">products</span>: [
      { <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">'苹果1斤'</span> },
      { <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">'橙子2个'</span> },
      { <span class="hljs-attr">id</span>: <span class="hljs-number">3</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">'香蕉3根'</span> }
    ],
    <span class="hljs-attr">totalCount</span>: <span class="hljs-number">0</span>
  }
});
</div></code></pre>
<pre class="hljs"><code><div>Vue.component(<span class="hljs-string">'product-item'</span>, {
  <span class="hljs-attr">props</span>: [<span class="hljs-string">'title'</span>],
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div&gt;
      &lt;span&gt;商品名称：{{ title }}，商品个数： {{ count }}&lt;/span&gt;
      &lt;button @click="countIns"&gt;+1&lt;/button&gt;
    &lt;/div&gt;
  `</span>,
  data () {
    <span class="hljs-keyword">return</span> { <span class="hljs-attr">count</span>: <span class="hljs-number">0</span> }
  },
  <span class="hljs-attr">methods</span>: {
    countIns () {
      <span class="hljs-keyword">this</span>.count++;
    }
  }
});
</div></code></pre>
<p>当商品个数变化时，我们应该将数据传递给父组件，这时可以通过触发自定义事件来实现。</p>
<p>方法 $emit() 用于触发指定的事件，父组件可以通过监听子组件的事件来得知子组件的状态变化。</p>
<pre class="hljs"><code><div>this.$emit('事件名');
</div></code></pre>
<p>注意，为确保事件名在组件与 DOM 模板内均得以生效，推荐<strong>始终使用 kebab-case 的事件名</strong>。</p>
<pre class="hljs"><code><div><span class="hljs-comment">// 子组件</span>
Vue.component(<span class="hljs-string">'product-item'</span>, {
  ...
  methods: {
    countIns () {
      <span class="hljs-keyword">this</span>.$emit(<span class="hljs-string">'count-change'</span>);
      <span class="hljs-keyword">this</span>.count++;
    }
  }
});
</div></code></pre>
<p>父组件监听子组件事件，并设置处理代码。</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  ...
  <span class="hljs-tag">&lt;<span class="hljs-name">product-item</span> 
	<span class="hljs-attr">...</span>
    @<span class="hljs-attr">count-change</span>=<span class="hljs-string">"totalCount++"</span>
  &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">product-item</span>&gt;</span>
  ...
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<h4 id="%E4%BA%8B%E4%BB%B6%E4%BC%A0%E5%80%BC">事件传值</h4>
<p>现在添加一个<code>+5</code> 按钮，如果 <code>+1</code> 和 <code>+5</code> 使用 count-change 事件，就需要准确告知父组件个数的变化，这时就需要通过事件进行传值。</p>
<pre class="hljs"><code><div><span class="hljs-keyword">this</span>.$emit(<span class="hljs-string">'事件名'</span>, <span class="hljs-string">'事件传值'</span>);
</div></code></pre>
<p>给两个按钮分别设置事件，并给 count-change 传递不同值。</p>
<pre class="hljs"><code><div>Vue.component(<span class="hljs-string">'product-item'</span>, {
  <span class="hljs-attr">props</span>: [<span class="hljs-string">'title'</span>],
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div&gt;
      &lt;span&gt;商品名称：{{ title }}，商品个数： {{ count }}&lt;/span&gt;
      &lt;button @click="countIns1"&gt;+1&lt;/button&gt;
      &lt;button @click="countIns5"&gt;+5&lt;/button&gt;
    &lt;/div&gt;
  `</span>,
  ...
  methods: {
    countIns1 () {
      <span class="hljs-keyword">this</span>.$emit(<span class="hljs-string">'count-change'</span>, <span class="hljs-number">1</span>);
      <span class="hljs-keyword">this</span>.count++;
    },
    countIns5 () {
      <span class="hljs-keyword">this</span>.$emit(<span class="hljs-string">'count-change'</span>, <span class="hljs-number">5</span>);
      <span class="hljs-keyword">this</span>.count+= <span class="hljs-number">5</span>;
    }
  }
});
</div></code></pre>
<p>父组件需要接受事件传值并进行相应处理。</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  ...
  <span class="hljs-tag">&lt;<span class="hljs-name">product-item</span>
    <span class="hljs-attr">...</span>
    @<span class="hljs-attr">count-change</span>=<span class="hljs-string">"totalCount += $event"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">product-item</span>&gt;</span>
  ...
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<p>或</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>购物车<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">product-item</span>
    <span class="hljs-attr">...</span>
    @<span class="hljs-attr">count-change</span>=<span class="hljs-string">"onCountChange"</span>
  &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">product-item</span>&gt;</span>
  ...
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  ...
  methods: {
    onCountChange (productCount) {
      <span class="hljs-keyword">this</span>.totalCount += productCount;
    }
  }
});
</div></code></pre>
<h4 id="%E7%BB%84%E4%BB%B6%E4%B8%8E-v-model">组件与 v-model</h4>
<p>v-model 用于双向数据绑定，但用于组件时，双向绑定的过程需要通过 props 与自定义事件实现。</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>输入内容为：{{ iptValue }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">com-input</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"iptValue"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">com-input</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> ComInput = {
  <span class="hljs-attr">props</span>: [<span class="hljs-string">'value'</span>],
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;input 
      type="text" 
      :value="value" 
      @input="$emit('input', $event.target.value)"&gt;`</span>
};
<span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">iptValue</span>: <span class="hljs-string">''</span>
  },
  <span class="hljs-attr">components</span>: {
    ComInput
  }
})
</div></code></pre>
<h3 id="%E9%9D%9E%E7%88%B6%E5%AD%90%E7%BB%84%E4%BB%B6%E4%BC%A0%E5%80%BC">非父子组件传值</h3>
<blockquote>
<p>非父子组件，例如一个父组件下的两个兄弟组件或两个没有任何关联的组件。</p>
</blockquote>
<h4 id="%E5%85%84%E5%BC%9F%E7%BB%84%E4%BB%B6%E4%BC%A0%E5%80%BC">兄弟组件传值</h4>
<p>兄弟组件可以将数据通过父组件进行中转，从而实现传值。</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">com-a</span> 
    @<span class="hljs-attr">value-change</span>=<span class="hljs-string">"value = $event"</span>
  &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">com-a</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">com-b</span>
    <span class="hljs-attr">:value</span>=<span class="hljs-string">"value"</span>
  &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">com-b</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div>Vue.component(<span class="hljs-string">'ComA'</span>, {
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div&gt;
      组件A的内容：{{ value }}
      &lt;button 
        @click="$emit('value-change', value)"
      &gt;发送&lt;/button&gt;
    &lt;/div&gt;`</span>,
  data () {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">value</span>: <span class="hljs-string">'示例内容'</span>
    }
  }
});
    
Vue.component(<span class="hljs-string">'ComB'</span>, {
  <span class="hljs-attr">props</span>: [<span class="hljs-string">'value'</span>],
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div&gt;
      组件B接收到：{{ value }}
    &lt;/div&gt;`</span>,
});
    
<span class="hljs-comment">// 根实例</span>
<span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">value</span>: <span class="hljs-string">''</span>
  }
});
</div></code></pre>
<p>但如果不是兄弟呢？这时再逐层传递数据会十分繁琐，且执行效率很低。</p>
<h4 id="eventbus">EventBus</h4>
<blockquote>
<p>EventBus（事件总线）是一个独立的事件中心，用来对不同组件间的传值操作进行管理。</p>
</blockquote>
<p>EventBus 通过一个独立的 Vue 实例（bus）来管理组件传值，不同组件传值时通过给 bus 设置事件、触发事件以达到数据传递的目的。总的来说，bus 中存储的只有用来传值的事件，而不存储数据。</p>
<p><strong>操作方式</strong></p>
<p>数据的传递过程一定会存在 <code>发送方</code> 与 <code>接收方</code>两个部分，接收方组件给 注册事件，发送方组件触发 bus 的对应事件，即可实现组件传值。</p>
<p>首先创建一个新的 Vue 实例作为 EventBus 的事件中心，通常设置为独立文件。</p>
<pre class="hljs"><code><div><span class="hljs-comment">// EventBus.js</span>
<span class="hljs-keyword">var</span> bus = <span class="hljs-keyword">new</span> Vue();
</div></code></pre>
<p>功能中，商品组件会进行个数操作，属于发送方，个数操作同时触发 bus 事件并传值。</p>
<pre class="hljs"><code><div><span class="hljs-comment">// 商品组件</span>
Vue.component(<span class="hljs-string">'product-item'</span>, {
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div&gt;
      &lt;span&gt;商品名称：苹果，商品个数： {{ count }}&lt;/span&gt;
      &lt;button @click="countIns"&gt;+1&lt;/button&gt;
    &lt;/div&gt;`</span>,
  data () {
    <span class="hljs-keyword">return</span> { <span class="hljs-attr">count</span>: <span class="hljs-number">0</span> }
  },
  <span class="hljs-attr">methods</span>: {
    countIns () {
      bus.$emit(<span class="hljs-string">'countChange'</span>, <span class="hljs-number">1</span>);
      <span class="hljs-keyword">this</span>.count++;
    },
  }
});
</div></code></pre>
<p>计数组件需要统计个数，属于接收方，当组件实例创建后给 bus 注册对应事件接收数据，在 created() 中通过 $on() 进行设置。</p>
<pre class="hljs"><code><div><span class="hljs-comment">// 计数组件</span>
Vue.component(<span class="hljs-string">'product-total'</span>, {
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;p&gt;总个数为：{{ totalCount }}&lt;/p&gt;
  `</span>,
  data () {
    <span class="hljs-keyword">return</span> { <span class="hljs-attr">totalCount</span>: <span class="hljs-number">0</span> }
  },
  created () {
    bus.$on(<span class="hljs-string">'countChange'</span>, (productCount) =&gt; {
      <span class="hljs-keyword">this</span>.totalCount += productCount;
    });
  }
});
</div></code></pre>
<p>最后创建根实例。</p>
<pre class="hljs"><code><div><span class="hljs-comment">// 根实例</span>
<span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
});
</div></code></pre>
<h3 id="%E5%85%B6%E4%BB%96%E9%80%9A%E4%BF%A1%E6%96%B9%E5%BC%8F">其他通信方式</h3>
<blockquote>
<p>下面的方式并不是推荐方式 ，只适合开发简单的 Vue 应用中使用，复杂应用使用可能会导致数据混乱难以维护，非必要情况不建议使用。这里作为补充讲解。</p>
</blockquote>
<h4 id="root">$root</h4>
<blockquote>
<p>$root 用于访问当前组件树根实例，设置简单的 Vue 应用时可以通过此方式进行组件传值。</p>
</blockquote>
<pre class="hljs"><code><div><span class="hljs-comment">&lt;!-- 父组件 --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>父组件数据： {{ count }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">com-a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">com-a</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">com-b</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">com-b</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-comment">// 父组件</span>
<span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">count</span>: <span class="hljs-number">0</span>
  },
  <span class="hljs-attr">components</span>: {
    ComA,
    ComB
  }
});
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-comment">// 组件 A: 要书写在根实例创建之前</span>
<span class="hljs-keyword">var</span> ComA = {
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div&gt;
      组件 A : {{ $root.count }}
      &lt;button @click="clickFn"&gt;+1&lt;/button&gt;
    &lt;/div&gt;
  `</span>,
  <span class="hljs-attr">methods</span>: {
    clickFn () {
      <span class="hljs-keyword">this</span>.$root.count++;
    }
  }
};
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-comment">// 组件 B: 要书写在根实例创建之前</span>
<span class="hljs-keyword">var</span> ComB = {
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div&gt;
      组件 B : {{ $root.count }}
      &lt;button @click="clickFn"&gt;+1&lt;/button&gt;
    &lt;/div&gt;
  `</span>,
  <span class="hljs-attr">methods</span>: {
    clickFn () {
      <span class="hljs-keyword">this</span>.$root.count++;
    }
  }
};
</div></code></pre>
<p>另外，Vue.js 中还提供了 $parent 与 $children 用于进行父子组件便捷访问，可以通过文档查询。</p>
<h4 id="refs">$refs</h4>
<blockquote>
<p>$refs 用于获取设置了 ref 属性的 HTML 标签或子组件。</p>
</blockquote>
<p>给普通  HTML 标签设置 ref 属性，可以通过 $refs 获取 DOM 对象。</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"inp"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"fn"</span>&gt;</span>按钮<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">methods</span>: {
    fn () {
      <span class="hljs-keyword">this</span>.$refs.inp.focus();
    }
  }
});
</div></code></pre>
<p>给子组件设置 ref 属性，通过 $refs 获取到的是子组件实例，需要在子组件渲染完毕后才能获取。</p>
<pre class="hljs"><code><div><span class="hljs-comment">&lt;!-- 父组件 --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">com-a</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"comA"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">com-a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-comment">// 父组件</span>
<span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,    
  <span class="hljs-attr">components</span>: {
    ComA
  },
  mounted () {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-keyword">this</span>.$refs);
    <span class="hljs-keyword">this</span>.$refs.comA.value = <span class="hljs-string">'修改子组件数据'</span>;
  },
});
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-comment">// 子组件</span>
<span class="hljs-keyword">var</span> ComA = {
  <span class="hljs-attr">template</span>: <span class="hljs-string">`&lt;p&gt;组件 A：{{ value }}&lt;/p&gt;`</span>,
  data () {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">value</span>: <span class="hljs-string">'这是组件A的数据'</span>
    }
  }
};
</div></code></pre>
<p>$refs 只会在组件渲染完成之后生效，且不是响应式数据，所以不要在模板或计算属性中使用。</p>
<h2 id="%E7%BB%84%E4%BB%B6%E6%8F%92%E6%A7%BD">组件插槽</h2>
<blockquote>
<p>组件插槽可以便捷的设置组件内容，让组件可以给 HTML 标签一样设置内容。</p>
</blockquote>
<h3 id="%E5%8D%95%E4%B8%AA%E6%8F%92%E6%A7%BD">单个插槽</h3>
<p>但平常我们书写的组件，组件首尾标签中书写的内容会被抛弃。</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">com-a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">com-a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<p>将组件中需要灵活设置内容的部分通过<code>&lt;slot&gt;&lt;/slot&gt; </code>设置为插槽，在组件标签中书写的任意内容都会替换掉模板中的 slot。可以为普通文本、标签或其他组件。</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">com-a</span>&gt;</span>
   	示例内容
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>组件的主体内容<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">com-a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div>Vue.component(<span class="hljs-string">'com-a'</span>, {
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div&gt;
	  &lt;h3&gt;组件标题&lt;/h3&gt;
      &lt;slot&gt;&lt;/slot&gt;
    &lt;/div&gt;
  `</span>
});
</div></code></pre>
<p>需要注意的是，插槽的内容是在父组件模板中书写的，就只能访问父组件数据，而无法访问子组件数据，因为：</p>
<blockquote>
<p>父级模板里的所有内容都是在父级作用域中编译的；子模板里的所有内容都是在子作用域中编译的。</p>
</blockquote>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">com-a</span>&gt;</span>
    这里只能访问父组件的数据
    {{ parValue }}
  <span class="hljs-tag">&lt;/<span class="hljs-name">com-a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,    
  <span class="hljs-attr">data</span>: { <span class="hljs-attr">parValue</span>: <span class="hljs-string">'父组件数据'</span> },
  <span class="hljs-attr">components</span>: {
    ComA
  },
});
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> ComA = {
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div&gt;
      &lt;p&gt;组件 A：&lt;/p&gt;
      &lt;slot&gt;&lt;/slot&gt;
    &lt;/div&gt;
  `</span>,
  data () {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">value</span>: <span class="hljs-string">'子组件数据'</span>
    }
  }
};
</div></code></pre>
<p><strong>后备内容（插槽默认值）</strong></p>
<p>可以在 <code>&lt;slot&gt;</code> 中设置后备内容（默认值），以备父组件没有设置插槽的具体数据。</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">com-a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">com-a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> ComA = {
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div&gt;
      &lt;p&gt;组件 A：&lt;/p&gt;
      &lt;slot&gt;这是默认文本&lt;/slot&gt;
    &lt;/div&gt;
  `</span>,
  data () {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">value</span>: <span class="hljs-string">'子组件数据'</span>
    }
  }
};
</div></code></pre>
<h3 id="%E5%85%B7%E5%90%8D%E6%8F%92%E6%A7%BD">具名插槽</h3>
<p>如果希望设置多个插槽，可以给 <code>&lt;slot&gt;</code> 设置 name 属性，未设置 name 的插槽默认 name 为 default。</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">slot</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"header"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">slot</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">slot</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">slot</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">footer</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">slot</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"footer"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">slot</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<p>使用时通过 <code>&lt;template&gt;</code> 包裹不同插槽内容，并设置 <code>v-slot</code> 指令传入对应的 name 参数即可。</p>
<p>注意：<code>v-slot</code> 只能作用于 <code>&lt;template&gt;</code></p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">com-a</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">v-slot:header</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>组件头部内容<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">v-slot:default</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>组件主体内容第一段<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>组件主体内容第二段<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">v-slot:footer</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>组件底部内容<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">com-a</span>&gt;</span>
</div></code></pre>
<p>任何没有被包裹在带有 <code>v-slot</code> 的 <code>&lt;template&gt;</code> 中的内容都会被视为默认插槽的内容</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">com-a</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">v-slot:header</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>组件头部内容<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>组件主体内容第一段<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>组件主体内容第二段<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">v-slot:footer</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>组件底部内容<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">com-a</span>&gt;</span>
</div></code></pre>
<p>Vue.js 为 <code>v-slot</code> 指令提供了简写方式，可以将 <code>v-slot:</code> 替换为 <code>#</code>。</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">com-a</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">header</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>组件头部内容<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>组件主体内容第一段<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>组件主体内容第二段<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">footer</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>组件底部内容<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">com-a</span>&gt;</span>
</div></code></pre>
<h3 id="%E4%BD%9C%E7%94%A8%E5%9F%9F%E6%8F%92%E6%A7%BD">作用域插槽</h3>
<blockquote>
<p>我们可以通过给 <code>slot</code> 绑定属性的方式将数据传递到插槽中，这样就可以在插槽中使用组件内的数据了。</p>
</blockquote>
<p>首先将需要被插槽访问的数据绑定给 <code>slot</code> 的属性，这种属性称为<strong>插槽 prop</strong>。</p>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> ComA = {
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div&gt;
      &lt;p&gt;组件 A：&lt;/p&gt;
      &lt;slot :value="value"&gt;这是默认文本&lt;/slot&gt;
    &lt;/div&gt;
  `</span>,
  data () {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">value</span>: <span class="hljs-string">'子组件数据'</span>
    }
  }
};
</div></code></pre>
<p>在插槽中通过 <code>v-slot</code> 可以给包含了所有插槽 prop 的对象命名。</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">com-a</span>&gt;</span>
  	<span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">v-slot:default</span>=<span class="hljs-string">"dataObj"</span>&gt;</span>  
      {{ dataObj.value }}
    <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">com-a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<p>很多情况下组件只有 default 插槽，这时可以将数据接收简写在组件标签上，这也是唯一一种可以将 <code>v-slot</code> 书写在 <code>&lt;template&gt;</code> 以外的情况。</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">com-a</span> <span class="hljs-attr">v-slot:default</span>=<span class="hljs-string">"dataObj"</span>&gt;</span>  
      {{ dataObj.value }}
  <span class="hljs-tag">&lt;/<span class="hljs-name">com-a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<p>或者简写为：注意，此处不能写成 #=“dataObj”，只有设置了命名的 slot 才能简写为 #。</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">com-a</span> <span class="hljs-attr">v-slot</span>=<span class="hljs-string">"dataObj"</span>&gt;</span>  
      {{ dataObj.value }}
  <span class="hljs-tag">&lt;/<span class="hljs-name">com-a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<p>也可以使用 ES6 的解构操作</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">com-a</span> <span class="hljs-attr">v-slot:default</span>=<span class="hljs-string">"{ value }"</span>&gt;</span>  
      {{ value }}
  <span class="hljs-tag">&lt;/<span class="hljs-name">com-a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<h2 id="%E5%86%85%E7%BD%AE%E7%BB%84%E4%BB%B6">内置组件</h2>
<h3 id="%E5%8A%A8%E6%80%81%E7%BB%84%E4%BB%B6">动态组件</h3>
<blockquote>
<p>动态组件 <code>&lt;component&gt;</code> 适用于多个组件需要频繁切换的场景。</p>
</blockquote>
<h4 id="is-%E5%B1%9E%E6%80%A7">is 属性</h4>
<p>用于实现多个组件的快速切换，例如选项卡效果。</p>
<pre class="hljs"><code><div><span class="hljs-comment">// 子组件</span>
<span class="hljs-keyword">var</span> ComA = { <span class="hljs-attr">template</span>: <span class="hljs-string">`&lt;div&gt;A组件内容&lt;/div&gt;`</span> };
<span class="hljs-keyword">var</span> ComB = { <span class="hljs-attr">template</span>: <span class="hljs-string">`&lt;div&gt;B组件内容&lt;/div&gt;`</span> };
<span class="hljs-keyword">var</span> ComC = { <span class="hljs-attr">template</span>: <span class="hljs-string">`&lt;div&gt;C组件内容&lt;/div&gt;`</span> };
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">titles</span>: [<span class="hljs-string">'ComA'</span>, <span class="hljs-string">'ComB'</span>, <span class="hljs-string">'ComC'</span>],
    <span class="hljs-attr">currentCom</span>: <span class="hljs-string">'ComA'</span>
  },
  <span class="hljs-attr">components</span>: {
    ComA, ComB, ComC
  }
});
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
    <span class="hljs-attr">v-for</span>=<span class="hljs-string">"title in titles"</span>
    <span class="hljs-attr">:key</span>=<span class="hljs-string">"title"</span>
    @<span class="hljs-attr">click</span>=<span class="hljs-string">"currentCom = title"</span>
  &gt;</span>
    {{title}}
  <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">component</span> <span class="hljs-attr">:is</span>=<span class="hljs-string">"currentCom"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">component</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<ul>
<li>
<p>is 属性在每次切换组件时会移除旧组件再创建新组件，不会因为组件切换导致状态保留。</p>
<pre class="hljs"><code><div>// 修改子组件代码为：
var ComA = {
  template: `<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>A组件内容：<span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>`
};
var ComB = {
  template: `<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>A组件内容：<span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>`
};
var ComC = {
  template: `<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>C组件内容：<span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>`
};
</div></code></pre>
</li>
</ul>
<h3 id="keep-alive-%E7%BB%84%E4%BB%B6">keep-alive 组件</h3>
<blockquote>
<p>主要用于保留组件状态或避免组件重新渲染。</p>
</blockquote>
<p>属性：</p>
<ul>
<li><code>include</code> - 字符串或正则表达式。只有名称匹配的组件会被缓存。</li>
<li><code>exclude</code> - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。</li>
<li><code>max</code> - 数字。最多可以缓存多少组件实例。</li>
</ul>
<p>具体效果演示见课程案例。</p>
<h3 id="%E8%BF%87%E6%B8%A1%E7%BB%84%E4%BB%B6">过渡组件</h3>
<blockquote>
<p>用于在 Vue 插入、更新或者移除 DOM 时，提供多种不同方式的应用过渡、动画效果。</p>
</blockquote>
<h4 id="transition-%E7%BB%84%E4%BB%B6">transition 组件</h4>
<p>Vue 提供了 <code>&lt;transition&gt;</code> 封装组件，在下列情形中，可以给任何元素和组件添加进入/离开过渡：</p>
<ul>
<li>条件渲染 (使用 <code>v-if</code>)</li>
<li>条件展示 (使用 <code>v-show</code>)</li>
<li>动态组件</li>
<li>组件根节点</li>
</ul>
<p>具体效果演示见课程案例。</p>
<h5 id="%E8%BF%87%E6%B8%A1%E7%9A%84%E7%B1%BB%E5%90%8D">过渡的类名</h5>
<blockquote>
<p>过渡动画分为入场动画和离场动画两部分，每部分动画都要指定开始、结束以及过程控制。</p>
<p>在 <code>&lt;transition&gt;</code> 进入/离开的过渡中，提供了 6个 class 切换，用于设置过渡的具体效果。</p>
</blockquote>
<h6 id="%E8%BF%9B%E5%85%A5%E7%9A%84%E7%B1%BB%E5%90%8D">进入的类名</h6>
<p><code>v-enter</code>（入场前的样式）</p>
<ul>
<li>定义进入过渡的开始状态。在元素被插入之前生效，在元素被插入之后的下一帧移除。</li>
</ul>
<p><code>v-enter-to</code>（入场完成后的样式）</p>
<ul>
<li>定义进入过渡的结束状态。在元素被插入之后下一帧生效 (同时 <code>v-enter</code> 被移除)，在过渡/动画完成后移除。</li>
</ul>
<p><code>v-enter-active</code>（入场过程控制）</p>
<ul>
<li>定义进入过渡生效时的状态。在整个进入过渡的阶段中应用，在元素被插入之前生效，在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间，延迟和曲线函数。</li>
</ul>
<h6 id="%E7%A6%BB%E5%BC%80%E7%9A%84%E7%B1%BB%E5%90%8D">离开的类名</h6>
<p><code>v-leave</code>（离场前的样式）</p>
<ul>
<li>定义离开过渡的开始状态。在离开过渡被触发时立刻生效，下一帧被移除。</li>
</ul>
<p><code>v-leave-to</code>（离开完成后的样式）</p>
<ul>
<li>定义离开过渡的结束状态。触发离开过渡的下一帧生效 (同时 <code>v-leave</code> 被删除)，在过渡/动画完成后移除。</li>
</ul>
<p><code>v-leave-active</code>（离场过程控制）</p>
<ul>
<li>定义离开过渡生效时的状态。在整个离开过渡的阶段中应用，在离开过渡被触发时立刻生效，在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间，延迟和曲线函数。</li>
</ul>
<blockquote>
<p>注意：当没有定义 v-enter-to 与 v-leave 时，默认会采用元素在标准流中的样式效果。</p>
</blockquote>
<h5 id="transition-%E7%9A%84%E5%B1%9E%E6%80%A7">transition 的属性</h5>
<p><strong>name 属性</strong></p>
<blockquote>
<p>给 <transition> 设置 name，可以给多个 transition 组件分别设置不同动画。</p>
</blockquote>
<p>当 <code>&lt;transition&gt;</code> 设置了 name 时，设置过渡的类名前缀要进行如下变化：</p>
<ul>
<li>
<p>当使用没有名字的 <code>&lt;transition&gt;</code> 时，所有类名默认为 <code>v-</code> 前缀。</p>
</li>
<li>
<p>当使用自定义 name 的 <code>&lt;transition&gt;</code> 时，所有类名以 <code>自定义name-</code> 为前缀。</p>
</li>
</ul>
<p><strong>appear 属性</strong></p>
<blockquote>
<p>用于设置 <transition> 在初始渲染时的过渡。</p>
</blockquote>
<h4 id="%E8%87%AA%E5%AE%9A%E4%B9%89%E8%BF%87%E6%B8%A1%E7%B1%BB%E5%90%8D">自定义过渡类名</h4>
<blockquote>
<p>Vue.js 允许我们设置比普通类名优先级高的自定义类名，在使用第三方 CSS 动画库时非常有用。</p>
</blockquote>
<p>用于设置自定义过渡类名的属性如下：</p>
<ul>
<li><code>enter-class</code></li>
<li><code>enter-active-class</code></li>
<li><code>enter-to-class</code></li>
<li><code>leave-class</code></li>
<li><code>leave-active-class</code></li>
<li><code>leave-to-class</code></li>
</ul>
<p>用于设置初始过渡类名的属性如下：</p>
<ul>
<li>
<p><code>appear-class</code></p>
</li>
<li>
<p><code>appear-to-class</code></p>
</li>
<li>
<p><code>appear-active-class</code></p>
</li>
</ul>
<h5 id="animatecss">Animate.css</h5>
<blockquote>
<p>Animate.css 是一个第三方 CSS 动画库，通过添加类名给元素设置各种动画效果。</p>
</blockquote>
<p>官网：https://animate.style/</p>
<p>使用注意：</p>
<ul>
<li>元素需要设置基础类名 animated 才能使用其他动画类名效果</li>
<li>4.0 版本后所有类名增加了 animate__ 前缀，让类名使用更加安全</li>
<li>提供了 compat 版本</li>
</ul>
<h4 id="transition-group-%E7%BB%84%E4%BB%B6">transition-group 组件</h4>
<blockquote>
<p><transition-group> 用于给一组元素（列表）统一设置过渡动画。</p>
</blockquote>
<p><code>&lt;transition-group&gt;</code> 与 <code>&lt;transition&gt;</code> 的不同之处：</p>
<ul>
<li>
<p><code>&lt;transition-group&gt;</code> 本身会创建为一个元素，默认为 <code>&lt;span&gt;</code> ，可通过 tag 属性更改。</p>
</li>
<li>
<p>CSS 过渡的类将会应用在内部的元素中，而不是这个组/容器本身。</p>
</li>
<li>
<p>每个 <code>&lt;transition-group&gt;</code> 的子节点必须有<strong>独立的 key</strong>，动画才能正常工作</p>
</li>
</ul>
<p>使用注意：</p>
<ul>
<li>所有过渡类名均与 <code>&lt;transition&gt;</code> 相同。</li>
<li>可通过 class <code>v-move</code> 设置列表项移动时的样式。</li>
</ul>
<h1 id="vue-router">Vue Router</h1>
<blockquote>
<p>Vue Router 是 Vue.js 的官方插件，用来快速实现单页应用。</p>
</blockquote>
<h2 id="%E5%8D%95%E9%A1%B5%E5%BA%94%E7%94%A8">单页应用</h2>
<blockquote>
<p>SPA（Single Page Application）单页面应用程序，简称单页应用。单页应用是网站功能的一种组织形式，与传统多页面网站不同，单页应用指的是网站的 “所有” 功能都在单个页面中进行呈现。</p>
</blockquote>
<p>通过观察示例网站可以发现 SPA 具有以下特点：</p>
<ul>
<li>
<p>优点：</p>
<ul>
<li>
<p>前后端分离开发，提高了开发效率。</p>
</li>
<li>
<p>业务场景切换时，局部更新结构。（无需刷新，可实现跳转动画）</p>
</li>
<li>
<p>用户体验好，更加接近本地应用。</p>
</li>
</ul>
</li>
<li>
<p>缺点：</p>
<ul>
<li>不利于 SEO。</li>
<li>初次首屏加载速度较慢。需要单独进行加载时间优化。
<ul>
<li>但多页共用的资源只需要加载一次</li>
</ul>
</li>
<li>功能集中在单页上，页面复杂度比较高。</li>
</ul>
</li>
</ul>
<h2 id="%E5%89%8D%E7%AB%AF%E8%B7%AF%E7%94%B1">前端路由</h2>
<blockquote>
<p>后端路由指的是 URL 与处理函数间的映射关系。前端路由与之类似，指的是 URL 与内容间的映射关系。</p>
</blockquote>
<p>前端路由的组成部分：</p>
<ul>
<li>n 个 URL</li>
<li>n 段内容（Vue 中为 n 个组件）</li>
<li>映射关系（对应关系）</li>
</ul>
<h2 id="%E5%89%8D%E7%AB%AF%E8%B7%AF%E7%94%B1%E7%9A%84%E5%AE%9E%E7%8E%B0">前端路由的实现</h2>
<blockquote>
<p>前端路由的原生实现有 Hash 与 History 两种方式。</p>
</blockquote>
<h3 id="hash-%E6%96%B9%E5%BC%8F">Hash 方式</h3>
<blockquote>
<p>Hash 方式是通过 hashchange 事件监听 hash 变化，从而实现网页功能更新。明显的标志是 URL 中带有 <code>#</code>。</p>
</blockquote>
<p>设置如下结构：</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#/"</span>&gt;</span>首页<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#/category"</span>&gt;</span>分类页<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#/user"</span>&gt;</span>用户页<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"container"</span>&gt;</span>
    这是首页功能
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
</div></code></pre>
<p>通过 onhashchange 事件监听 hash 变化，并进行页面内容更新。</p>
<pre class="hljs"><code><div><span class="hljs-built_in">window</span>.onhashchange = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">var</span> hash = location.hash.replace(<span class="hljs-string">'#'</span>, <span class="hljs-string">''</span>);
  <span class="hljs-keyword">var</span> str = <span class="hljs-string">''</span>
  <span class="hljs-keyword">switch</span> (hash) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">'/'</span>:
      str = <span class="hljs-string">'这是首页的功能'</span>;
      <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">case</span> <span class="hljs-string">'/category'</span>:
      str = <span class="hljs-string">'这是分类的功能'</span>;
      <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">case</span> <span class="hljs-string">'/user'</span>:
      str = <span class="hljs-string">'这是用户的功能'</span>;
      <span class="hljs-keyword">break</span>;
  }
  <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'container'</span>).innerHTML = str;
};
</div></code></pre>
<p>对功能进行封装</p>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> router = {
  <span class="hljs-comment">// 路由存储位置</span>
  <span class="hljs-attr">routes</span>: {},
  <span class="hljs-comment">// 定义路由</span>
  <span class="hljs-attr">route</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>  (<span class="hljs-params">path, callback</span>) </span>{
    <span class="hljs-keyword">this</span>.routes[path] = callback;
  },
  <span class="hljs-comment">// 初始化路由</span>
  <span class="hljs-attr">init</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">var</span> that = <span class="hljs-keyword">this</span>;
    <span class="hljs-built_in">window</span>.onhashchange = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      <span class="hljs-keyword">var</span> hash = location.hash.replace(<span class="hljs-string">'#'</span>, <span class="hljs-string">''</span>);
      that.routes[hash] &amp;&amp; that.routes[hash]();
    };
  }
};
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> container = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'container'</span>);
<span class="hljs-comment">// 定义路由</span>
router.route(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  container.innerHTML = <span class="hljs-string">'这是首页的功能'</span>;
});
router.route(<span class="hljs-string">'/category'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  container.innerHTML = <span class="hljs-string">'这是分类的功能'</span>;
});
router.route(<span class="hljs-string">'/user'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  container.innerHTML = <span class="hljs-string">'这是用户的功能'</span>;
});   
<span class="hljs-comment">// 初始化路由</span>
router.init();
</div></code></pre>
<ul>
<li>以上为基础功能，不包含前进后退，分析后可得知前进后退的实现较为繁琐。</li>
</ul>
<p>这里总结 Hash 方式特点：</p>
<ul>
<li>Hash 方式兼容性好。</li>
<li>地址中具有 #，不太美观。</li>
<li>前进后退功能较为繁琐。</li>
</ul>
<h3 id="history-%E6%96%B9%E5%BC%8F">History 方式</h3>
<blockquote>
<p>History 方式实现为 HTML5 提供的新功能，是前端路由实现的新标准。</p>
</blockquote>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/"</span>&gt;</span>首页<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/category"</span>&gt;</span>分类页<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/user"</span>&gt;</span>用户页<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"container"</span>&gt;</span>
    这是首页功能
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
</div></code></pre>
<p>由于 History 不是 a 标签的默认行为，所以在操作时需要通过 history.pushState() 变更 URL。</p>
<p>pushState() 参数如下</p>
<ul>
<li>state ：与指定路径相关的<strong>状态对象</strong>，popstate 事件触发时，可通过事件对象的 state 接收副本。不需要时填 null。
title：浏览器还不支持，填 '' （空字符串）即可。
URL：新的网址，必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> router = {
  <span class="hljs-comment">// 存储路由</span>
  <span class="hljs-attr">routes</span>: {},
  <span class="hljs-comment">// 定义路由</span>
  <span class="hljs-attr">route</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">path, callback</span>) </span>{
    <span class="hljs-keyword">this</span>.routes[path] = callback;
  },
  <span class="hljs-comment">// 触发指定路由规则</span>
  <span class="hljs-attr">go</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">path</span>) </span>{
    history.pushState(<span class="hljs-literal">null</span>, <span class="hljs-literal">null</span>, path);
    <span class="hljs-keyword">this</span>.routes[path] &amp;&amp; <span class="hljs-keyword">this</span>.routes[path]();
  }
}
</div></code></pre>
<p>设置测试功能，注意要阻止 a 标签默认跳转。</p>
<pre class="hljs"><code><div><span class="hljs-comment">// 使用测试</span>
<span class="hljs-keyword">var</span> links = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">'a'</span>);
<span class="hljs-keyword">var</span> container = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'#container'</span>);    
links.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">ele</span>) </span>{
  ele.onclick = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>) </span>{
    <span class="hljs-keyword">var</span> path = e.target.getAttribute(<span class="hljs-string">'href'</span>);
    <span class="hljs-comment">// 调用路由</span>
    router.go(path);
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>; <span class="hljs-comment">// 阻止跳转</span>
  }
}); 

<span class="hljs-comment">// 定义路由规则</span>
router.route(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  container.innerHTML = <span class="hljs-string">'这是首页的功能'</span>;
});
router.route(<span class="hljs-string">'/category'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  container.innerHTML = <span class="hljs-string">'这是分类的功能'</span>;
});
router.route(<span class="hljs-string">'/user'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  container.innerHTML = <span class="hljs-string">'这是用户的功能'</span>;
});   
</div></code></pre>
<p>实现前进后退功能时，需要通过 pushState() 的 state 传递路由标记，并通过 popstate 事件的事件对象访问 state 来获取路由标记，并触发对应功能。</p>
<pre class="hljs"><code><div>go: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">path</span>) </span>{
  history.pushState({ <span class="hljs-attr">path</span>: path }, <span class="hljs-literal">null</span>, path);
  ...
}
</div></code></pre>
<p>设置 init 方法，监听前进后退的触发。</p>
<pre class="hljs"><code><div>init: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">var</span> that = <span class="hljs-keyword">this</span>;
  <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'popstate'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>) </span>{
    <span class="hljs-keyword">var</span> path = e.state ? e.state.path: <span class="hljs-string">'/'</span>;
    that.routes[path] &amp;&amp; that.routes[path]();
  });
}
</div></code></pre>
<p>进行初始调用</p>
<pre class="hljs"><code><div>...
router.init();
...
</div></code></pre>
<p>这里总结 History 方式特点：</p>
<ul>
<li>实现方式简洁。</li>
<li>在 URL 显示上更符合前端路由格式。</li>
<li>pushState() 可存储序列化后最大 640k 的数据，hash 方式为 2k。</li>
<li>兼容性差，ie10 开始支持。</li>
<li>刷新时会访问对应路径，需要后端配合处理。</li>
</ul>
<h2 id="vue-router">Vue Router</h2>
<blockquote>
<p>Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成，让构建单页面应用变得易如反掌。</p>
</blockquote>
<p>中文官网地址：https://router.vuejs.org/zh/</p>
<h3 id="%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8">基本使用</h3>
<p>直接下载 / CDN</p>
<ul>
<li>
<p>最新版本：https://unpkg.com/vue-router/dist/vue-router.js</p>
</li>
<li>
<p>指定版本：https://unpkg.com/vue-router@3.4.9/dist/vue-router.js</p>
</li>
</ul>
<p>npm</p>
<pre class="hljs"><code><div>npm install vue-router
</div></code></pre>
<p>Vue Router 提供了用于进行路由设置的组件 <code>&lt;router-link&gt;</code> 与 <code>&lt;router-view&gt;</code>。</p>
<ul>
<li>使用 <code>&lt;router-link&gt;</code> 组件来导航，用 <code>to</code> 属性指定链接，组件默认会渲染为 <code>&lt;a&gt;</code> 标签，可通过 tag 属性设置为其他标签。</li>
<li>Vue Router 的路由是以组件的单位的，<code>&lt;router-view&gt;</code> 用来显示路由匹配到的组件。</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>&gt;</span>首页<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/category"</span>&gt;</span>分类<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/user"</span>&gt;</span>用户<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">router-view</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">router-view</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<p>提前定义路由中的组件（配置对象）。</p>
<pre class="hljs"><code><div>var Index = {
  template: `<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>这是首页的功能<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>`
};
var Category = {
  template: `<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>这是分类的功能<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>`
};
var User = {
  template: `<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>这是用户的功能<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>`
};
</div></code></pre>
<p>定义路由规则</p>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> routes = [
  { <span class="hljs-attr">path</span>: <span class="hljs-string">'/'</span>, <span class="hljs-attr">component</span>: Index },
  { <span class="hljs-attr">path</span>: <span class="hljs-string">'/category'</span>, <span class="hljs-attr">component</span>: Category },
  { <span class="hljs-attr">path</span>: <span class="hljs-string">'/user'</span>, <span class="hljs-attr">component</span>: User }
];
</div></code></pre>
<p>创建 Vue Router 实例，通过 routes 属性配置路由</p>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> router = <span class="hljs-keyword">new</span> VueRouter({
  <span class="hljs-attr">routes</span>: routes
});
</div></code></pre>
<p>创建 Vue 实例，通过 router 属性注入路由</p>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue({
  <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
  <span class="hljs-attr">router</span>: router
});
</div></code></pre>
<blockquote>
<p>输出 Vue 实例，观察 $route 与 $router</p>
</blockquote>
<h4 id="%E5%91%BD%E5%90%8D%E8%A7%86%E5%9B%BE">命名视图</h4>
<blockquote>
<p>如果导航后，希望同时在同级展示多个视图（组件），这时就需要进行命名视图。</p>
</blockquote>
<p>多个视图通过多个 <code>&lt;router-view&gt;</code> 表示，并设置不同的 name 属性；如果未设置，默认为 default。</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>&gt;</span>首页<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/user"</span>&gt;</span>用户<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
    
  <span class="hljs-tag">&lt;<span class="hljs-name">router-view</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"sidebar"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">router-view</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">router-view</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">router-view</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<p>设置命名视图后，路由中通过 components 属性进行设置（注意不是 component，而是 components）。</p>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> SideBar = {
  <span class="hljs-attr">template</span>: <span class="hljs-string">`&lt;div&gt;这是侧边栏功能&lt;/div&gt;`</span>
};
<span class="hljs-comment">// 定义路由规则</span>
<span class="hljs-keyword">var</span> routes = [
  { 
    <span class="hljs-attr">path</span>: <span class="hljs-string">'/'</span>, 
    <span class="hljs-attr">components</span>: {
      <span class="hljs-attr">sidebar</span>: SideBar,
      <span class="hljs-attr">default</span>: Index
    }
  },
  {
    <span class="hljs-attr">path</span>: <span class="hljs-string">'/user'</span>, 
    <span class="hljs-attr">components</span>: {
      <span class="hljs-attr">sidebar</span>: SideBar,
      <span class="hljs-attr">default</span>: User
    }
  }
];
</div></code></pre>
<h3 id="%E5%8A%A8%E6%80%81%E8%B7%AF%E7%94%B1">动态路由</h3>
<blockquote>
<p>当我们需要将某一类 URL 都映射到同一个组件，就需要使用动态路由。</p>
</blockquote>
<p>定义路由规则时，将路径中的某个部分使用 <code>:</code> 进行标记，即可设置为动态路由。</p>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> User = {
  <span class="hljs-attr">template</span>: <span class="hljs-string">`&lt;div&gt;这是用户的功能&lt;/div&gt;`</span>
};
<span class="hljs-keyword">var</span> routes = [
  { <span class="hljs-attr">path</span>: <span class="hljs-string">'/user/:id'</span>, <span class="hljs-attr">component</span>: User }
];
</div></code></pre>
<p>设置为动态路由后，动态部分为任意内容均跳转到同一组件。（示例中为用户名或用户 ID）</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/user/1"</span>&gt;</span>用户1<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/user/2"</span>&gt;</span>用户2<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/user/3"</span>&gt;</span>用户3<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">router-view</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">router-view</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</div></code></pre>
<p>带有 <code>:</code> 的部分对应的信息称为 <code>路径参数</code>，存储在 vm.$route.params 中。</p>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> User = {
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
	&lt;div&gt;
	  这是用户 {{ $route.params.id }} 的功能
	&lt;/div&gt;`</span>
};
</div></code></pre>
<h4 id="%E4%BE%A6%E5%90%AC%E8%B7%AF%E7%94%B1%E5%8F%82%E6%95%B0">侦听路由参数</h4>
<blockquote>
<p>使用动态路由时，如果仅为路由参数变化，组件实例会被复用而不是销毁后再重新创建。这意味着组件的生命周期钩子不会再调用。</p>
</blockquote>
<p>如果希望在路由参数变化时作出响应，可以通过 watch 监听 $route。</p>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> User = {
  <span class="hljs-attr">template</span>: <span class="hljs-string">`&lt;div&gt;这是 {{ $route.params.id }} 功能&lt;/div&gt;`</span>,
  <span class="hljs-attr">watch</span>: {
    $route (route) {
      <span class="hljs-built_in">console</span>.log(route);
    }
  }
};
</div></code></pre>
<h4 id="%E8%B7%AF%E7%94%B1%E4%BC%A0%E5%8F%82%E5%A4%84%E7%90%86">路由传参处理</h4>
<blockquote>
<p>在组件中使用 $route 会让组件与路由高度耦合，如果希望组件可以在其他路由中使用，需要进行解耦。</p>
</blockquote>
<p>通过路由的 props 设置数据，并通过组件 props 接收，从而将组件与路由解耦。本质上是将 $route.params 的值传递给组件 props。</p>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> routes = [
  { 
    <span class="hljs-attr">path</span>: <span class="hljs-string">'/user/:id'</span>,
    <span class="hljs-attr">component</span>: User
  },
  { 
    <span class="hljs-attr">path</span>: <span class="hljs-string">'/category/:id'</span>, 
    <span class="hljs-attr">component</span>: Category,
    <span class="hljs-comment">// props: true 表示将路径参数设置为组件属性（而组件属性可以通过组件的 props 接收）</span>
    <span class="hljs-attr">props</span>: <span class="hljs-literal">true</span>
  },
];
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> User = {
  <span class="hljs-attr">template</span>: <span class="hljs-string">`&lt;div&gt;这是 {{ $route.params.id }} 的功能&lt;/div&gt;`</span>
};
<span class="hljs-keyword">var</span> Category = {
  <span class="hljs-attr">props</span>: [<span class="hljs-string">'id'</span>],
  <span class="hljs-attr">template</span>: <span class="hljs-string">`&lt;div&gt;这是 {{ id }} 功能&lt;/div&gt;`</span>
};
</div></code></pre>
<p>如果路由包含多个命名视图，则需要将路由的 props 设置为对象，以给每个视图添加 props 选项。</p>
<pre class="hljs"><code><div>{ 
  <span class="hljs-attr">path</span>: <span class="hljs-string">'/category/:id'</span>, 
  <span class="hljs-attr">components</span>: {
    <span class="hljs-attr">default</span>: Category,
    <span class="hljs-attr">sidebar</span>: SideBar
  },
  <span class="hljs-attr">props</span>: {
    <span class="hljs-attr">default</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">sidebar</span>: <span class="hljs-literal">false</span> 
  }
}
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> SideBar = {
  <span class="hljs-attr">template</span>: <span class="hljs-string">`&lt;div&gt;这是侧边栏功能&lt;/div&gt;`</span>
};
</div></code></pre>
<p>如果希望设置静态数据，可将 props 中的某个组件对应的选项设置为对象，内部属性会绑定给组件的 props。</p>
<pre class="hljs"><code><div>{ 
  <span class="hljs-attr">path</span>: <span class="hljs-string">'/category/:id'</span>, 
  <span class="hljs-attr">components</span>: {
    <span class="hljs-attr">default</span>: Category,
    <span class="hljs-attr">sidebar</span>: SideBar,
    <span class="hljs-attr">sidebar2</span>: SideBar2
  },
  <span class="hljs-attr">props</span>: {
    <span class="hljs-attr">default</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">sidebar</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">sidebar2</span>: { <span class="hljs-attr">a</span>: <span class="hljs-string">'状态1'</span>,<span class="hljs-attr">b</span>: <span class="hljs-string">'状态2'</span>}
  }
},
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> SideBar2 = {
  <span class="hljs-attr">props</span>: [<span class="hljs-string">'a'</span>, <span class="hljs-string">'b'</span>, <span class="hljs-string">'c'</span>, <span class="hljs-string">'id'</span>],
  <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div&gt;
      这是右侧侧边栏功能: {{ a }} {{ b }} {{ c }}
    &lt;/div&gt;
  `</span>
};
</div></code></pre>
<h3 id="%E5%B5%8C%E5%A5%97%E8%B7%AF%E7%94%B1">嵌套路由</h3>
<blockquote>
<p>实例场景中，路由通常由多层嵌套的组件组合而成，这时需要使用嵌套路由配置。</p>
</blockquote>
<p>使用 children 来进行嵌套路由中的子路由设置。</p>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> routes = [
  { 
    <span class="hljs-attr">path</span>: <span class="hljs-string">'/user'</span>, 
    <span class="hljs-attr">component</span>: User,
    <span class="hljs-attr">children</span>: [
      { 
        <span class="hljs-attr">path</span>: <span class="hljs-string">'hobby'</span>, 
        <span class="hljs-attr">component</span>: UserHobby
      },
      { 
        <span class="hljs-attr">path</span>: <span class="hljs-string">'info'</span>, 
        <span class="hljs-attr">component</span>: UserInfo,
        <span class="hljs-attr">children</span>: [
          { <span class="hljs-attr">path</span>: <span class="hljs-string">'age'</span>, <span class="hljs-attr">component</span>: UserInfoAge },
          { <span class="hljs-attr">path</span>: <span class="hljs-string">'school'</span>, <span class="hljs-attr">component</span>: UserInfoSchool },
        ]
      }
    ]
  }
];
</div></code></pre>
<h3 id="%E7%BC%96%E7%A8%8B%E5%BC%8F%E5%AF%BC%E8%88%AA">编程式导航</h3>
<blockquote>
<p>除了可以通过 <code>&lt;router-link&gt;</code> 来定义声明式导航，还可以通过 router 实例方法设置编程式导航。</p>
</blockquote>
<p>router.push() 用来导航到一个新 URL。</p>
<ul>
<li>
<p>参数为路径信息，可以为字符串或对象</p>
<pre class="hljs"><code><div>vm.$router.push(<span class="hljs-string">'/user'</span>);
vm.$router.push({<span class="hljs-attr">path</span>: <span class="hljs-string">'/user'</span>});
vm.$router.push({<span class="hljs-attr">path</span>: <span class="hljs-string">'/user/123'</span>});
</div></code></pre>
</li>
<li>
<p>可以将 <code>&lt;router-link&gt;</code> 的导航设置在 <code>:to</code> 属性中，点击时内部也会调用 push()。</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">:to</span>=<span class="hljs-string">"{ path: '/user/10' }"</span>&gt;</span>用户10<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
</div></code></pre>
</li>
</ul>
<p>除了 push 外，Vue Router 还提供了 replace()、go()、forward()、back() 方法，可查询文档学习。</p>
<h4 id="%E5%91%BD%E5%90%8D%E8%B7%AF%E7%94%B1">命名路由</h4>
<blockquote>
<p>有时候，通过名称来表示一个路由更加方便，尤其是在导航到一些较长的路由的时候（如嵌套路由时）。</p>
</blockquote>
<p>设置路由时添加 name 属性。</p>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> School = {
  <span class="hljs-attr">template</span>: <span class="hljs-string">`&lt;div&gt;School 组件：{{ $route.params }}&lt;/div&gt;`</span>
};
<span class="hljs-keyword">var</span> routes = [
  {
    <span class="hljs-attr">path</span>: <span class="hljs-string">'/user/:id/info/school'</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">'school'</span>,
    <span class="hljs-attr">component</span>: School
  }
];
</div></code></pre>
<p>在 push() 中通过 name 导航到对应路由，参数通过 params 对象设置。（编程式导航）</p>
<ul>
<li>params <strong>只能与 name 搭配使用</strong>，如果使用 path 则 params 无效。</li>
</ul>
<pre class="hljs"><code><div>vm.$router.push({ <span class="hljs-attr">name</span>: <span class="hljs-string">'school'</span>, <span class="hljs-attr">params</span>: { <span class="hljs-attr">id</span>: <span class="hljs-number">20</span>, <span class="hljs-attr">demo</span>: <span class="hljs-string">'其他数据'</span> }});
</div></code></pre>
<p>也可以在 <code>&lt;router-link&gt;</code> 中使用。（声明式导航）</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">:to</span>=<span class="hljs-string">"{ name: 'school', params: { id: 1 } }"</span>&gt;</span>用户学校<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">:to</span>=<span class="hljs-string">"{ name: 'school', params: { id: 2 } }"</span>&gt;</span>用户学校<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">:to</span>=<span class="hljs-string">"{ name: 'school', params: { id: 3 } }"</span>&gt;</span>用户学校<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
</div></code></pre>
<h3 id="%E5%85%B6%E4%BB%96%E5%8A%9F%E8%83%BD">其他功能</h3>
<h4 id="%E9%87%8D%E5%AE%9A%E5%90%91">重定向</h4>
<blockquote>
<p>用户访问 URL1 时，实际被替换为了 URL2 并匹配了对应路由，这就是重定向。</p>
</blockquote>
<p>例如，设置路由 <code>/category/:id</code> 用来访问某个分类功能，但如果 URL 为 <code>/category</code> 则没有任何意义，这时可以进行重定向。</p>
<pre class="hljs"><code><div> <span class="hljs-keyword">var</span> routes = [
   { <span class="hljs-attr">path</span>: <span class="hljs-string">'/'</span>, <span class="hljs-attr">component</span>: Index},
   { <span class="hljs-attr">path</span>: <span class="hljs-string">'/category/:id'</span>, <span class="hljs-attr">component</span>: Category },
   { <span class="hljs-attr">path</span>: <span class="hljs-string">'/category'</span>, <span class="hljs-attr">redirect</span>: <span class="hljs-string">'/'</span> }
 ];
</div></code></pre>
<h4 id="%E5%88%AB%E5%90%8D">别名</h4>
<blockquote>
<p>别名是一种美化路由的方式。</p>
</blockquote>
<p>别名指的是用户访问 URL1 时，实际上匹配了 URL2 对应的路由，但 URL 还保持为 URL1。假定有路由 /user/:id/info/school/intro/:date 这时 URL 看起来十分复杂，我们可以设置为别名为例如 /10/20200910。</p>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> routes = [
  {
    <span class="hljs-attr">path</span>: <span class="hljs-string">'/user/:id/info/school/intro/:date'</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">'school'</span>,
    <span class="hljs-attr">component</span>: School,
    <span class="hljs-attr">alias</span>: <span class="hljs-string">'/:id/:date'</span>
  }
];
</div></code></pre>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">:to</span>=<span class="hljs-string">"{ name: 'school', params: { id: 1, date: 0101 } }"</span>&gt;</span>用户学校<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/10/0612"</span>&gt;</span>用户学校<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
</div></code></pre>
<h4 id="%E5%AF%BC%E8%88%AA%E5%AE%88%E5%8D%AB">导航守卫</h4>
<blockquote>
<p>导航守卫用于在路由发生改变时，通过跳转或取消的方式来守卫导航。</p>
</blockquote>
<p>例如，网站的用户页，如果用户没有登录，则无法访问，这时就可以给 router 使用导航守卫。</p>
<pre class="hljs"><code><div>router.beforeEach(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">to, from, next</span>) </span>{
  <span class="hljs-built_in">console</span>.log(to, <span class="hljs-keyword">from</span>);
  next();
});
</div></code></pre>
<p>next() 用来继续执行后续功能</p>
<p>next(false) 用于阻止本次导航，</p>
<p><code>next('/')</code> 者 <code>next({ path: '/' })</code> 用于中止本次导航，并进行一个新的导航。</p>
<p>必须确保 <code>next</code> 在任何导航守卫中都被严格调用一次（最多一次，最少也一次）。</p>
<h4 id="history-%E6%A8%A1%E5%BC%8F">History 模式</h4>
<blockquote>
<p><code>vue-router</code> 默认 hash 模式，如果需要，可通过实例属性设置为 History 模式。</p>
</blockquote>
<p>需要通过 Vue Router 实例的 <code>mode</code> 选项来设置，这样 URL 会更加美观，但同样需要后端支持避免问题。</p>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> router = <span class="hljs-keyword">new</span> VueRouter({
  <span class="hljs-attr">mode</span>: <span class="hljs-string">'history'</span>,
  <span class="hljs-attr">routes</span>: [
    ...
  ]
});
</div></code></pre>
<h1 id="vue-cli">Vue CLI</h1>
<blockquote>
<p>Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统，称为脚手架工具。</p>
</blockquote>
<p>官网：https://cli.vuejs.org/zh/</p>
<p>作用：</p>
<ul>
<li>提供单文件组件（优化组件书写方式）</li>
<li>统一项目的架构风格。</li>
<li>初始化配置项目依赖</li>
</ul>
<p>操作方式：命令行工具</p>
<h2 id="%E5%AE%89%E8%A3%85%E4%B8%8E%E5%8D%87%E7%BA%A7">安装与升级</h2>
<blockquote>
<p>Vue CLI 是命令行工具，需要全局安装。</p>
</blockquote>
<p>npm</p>
<ul>
<li>
<p>安装</p>
<pre class="hljs"><code><div>npm install -g @vue/cli
</div></code></pre>
</li>
<li>
<p>升级</p>
<pre class="hljs"><code><div>npm update -g @vue/cli
</div></code></pre>
</li>
</ul>
<h2 id="%E9%A1%B9%E7%9B%AE%E6%90%AD%E5%BB%BA">项目搭建</h2>
<ul>
<li>
<p>创建项目</p>
<pre class="hljs"><code><div>vue create project-demo
</div></code></pre>
</li>
<li>
<p>选择 Preset（预设）</p>
</li>
</ul>
<p><img src="./media/vue-cli/1.png" alt=""></p>
<ul>
<li>
<p>选择包管理器</p>
<p><img src="./media/vue-cli/2.png" alt=""></p>
</li>
<li>
<p>创建完成</p>
<p><img src="./media/vue-cli/3.png" alt=""></p>
</li>
<li>
<p>运行项目（注意目录）</p>
<pre class="hljs"><code><div>npm run serve
</div></code></pre>
</li>
<li>
<p>项目运行成功</p>
<p><img src="media/vue-cli/4.png" alt=""></p>
</li>
</ul>
<h2 id="%E7%9B%AE%E5%BD%95%E4%B8%8E%E6%96%87%E4%BB%B6">目录与文件</h2>
<p>注：下面为主要目录与文件含义</p>
<pre class="hljs"><code><div>└─ 根目录
   ├─ node_modules
   ├─ public 		   预览文件目录
   └─ src    
      ├─ assets 	   静态资源目录
      └─ components    项目组件目录
   	  └─ App.vue       根组件
      └─ main.js       入口文件
</div></code></pre>
<h3 id="%E5%8D%95%E6%96%87%E4%BB%B6%E7%BB%84%E4%BB%B6">单文件组件</h3>
<blockquote>
<p>组件是 Vue 应用的基础，每个组件都由模板、样式、逻辑 3 部分组成。Vue CLI 中提供了以 .vue 为扩展名的文件作为组件的载体，使得组件更加内聚且更可维护，这种形式称为单文件组件。</p>
</blockquote>
<p>传统组件定义方式的缺点：</p>
<ul>
<li>
<p>字符串模板缺乏语法高亮，在 HTML 有多行的时候，需要用到<code>\</code> 或使用兼容性差的模板字符串。</p>
</li>
<li>
<p>不支持 CSS 意味着当 HTML 和 JavaScript 组件化时，CSS 明显被遗漏。</p>
</li>
<li>
<p>没有构建步骤，限制只能使用 HTML 和 ES5 JavaScript，而不能使用预处理器，如 Babel 等工具。</p>
</li>
</ul>
<p>单文件组件的特点：</p>
<ul>
<li>文件扩展名为 <code>.vue</code></li>
<li>内部通过 <code>&lt;template&gt;</code> <code>&lt;script&gt;</code> <code>&lt;style&gt;</code>  分别进行模板、逻辑、样式设置。</li>
<li>每个单文件组件均为独立模块，导入导出需要使用模块语法。</li>
</ul>
<p>单文件组件书写示例：</p>
<pre class="hljs"><code><div>&lt;template&gt;
  &lt;p&gt;组件模板&lt;/p&gt;
&lt;/template&gt;

&lt;script&gt;
  // 组件逻辑（这里采用了 ES6 Module）
  export default {
    name: &quot;MyComponent&quot;
  };
&lt;/script&gt;

&lt;style scoped&gt;
  /* 组件样式 */
  p {
    font-size: 20px;
    text-align: center;
  }
&lt;/style&gt;
</div></code></pre>
<p>使用 Vue CLI 后，每个组件都是独立模块，在操作时需要通过模块语法进行导入导出（如 ES6 Module）</p>
<ul>
<li>
<p>导入</p>
<pre class="hljs"><code><div><span class="hljs-keyword">import</span> Demo <span class="hljs-keyword">from</span> <span class="hljs-string">'Demo.vue'</span>
</div></code></pre>
</li>
<li>
<p>导出</p>
<pre class="hljs"><code><div><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  data () {
    <span class="hljs-keyword">return</span> {
      ...
    }
  },
  <span class="hljs-attr">methods</span>: {
    ...
  }
};
</div></code></pre>
</li>
</ul>
<h2 id="%E6%89%93%E5%8C%85%E4%B8%8E%E9%83%A8%E7%BD%B2">打包与部署</h2>
<h3 id="%E6%89%93%E5%8C%85">打包</h3>
<blockquote>
<p>我们通过 Vue CLI 创建的项目中都是使用 .vue 文件开发的，这种文件无法部署上线被用户访问（浏览器不认识），这时需要 Vue CLI 将项目编译为浏览器可识别的文件（HTML、CSS、JS）。</p>
</blockquote>
<p>命令：</p>
<pre class="hljs"><code><div>npm run build
</div></code></pre>
<h3 id="%E9%83%A8%E7%BD%B2">部署</h3>
<blockquote>
<p>部署指的是将 Vue 项目dist 目录部署到服务器上。</p>
</blockquote>
<p>这里我们主要说一下如何在本地部署并预览项目的方式。</p>
<p>因为 dist 目录默认需要启动 HTTP 服务器来访问，所以无法采用 '双击'  以 file 协议运行。比较常用的方式是使用 Node.js 静态文件服务器。</p>
<p>首先安装 serve</p>
<pre class="hljs"><code><div>npm install -g serve
</div></code></pre>
<p>启动服务器</p>
<pre class="hljs"><code><div>工作路径为 dist 父级时的命令：
serve dist 
工作路径为 dist 时的命令：
serve
</div></code></pre>

</body>
</html>
